はじめに
GitHub Actions で AWS Batch のデプロイワークフローを構築した際、ジョブ定義ファイル内のプレースホルダーを sed で置換する方法を使いました。sed を使用するときの注意点についてまとめます🐣
💡 この記事でわかること
- sed の基本
- sed の区切り文字の選び方とメタ文字の注意点
- GitHub Actions でジョブ定義を sed で置換する例
sed の基本
sed とは
sed(Stream Editor)は、テキストの検索・置換を行う Unix / Linux のコマンドです。
GitHub Actionsの実行環境には標準で入っているため、そのまま使うことができます。
基本構文
以下のコマンドで、ファイル内の検索文字列を置換文字列に置き換えます
sed "s/検索文字列/置換文字列/" ファイル名
-
sは substitute(置換) の頭文字 -
/は区切り文字 - デフォルトでは、各行の最初にマッチした文字列のみ置換する
sed は行単位で処理する
sed は、入力ファイルに対して、 1行読む → 処理する → 出力する を繰り返し行います。
入力ファイル:
1行目 → sed が読み込み → 置換ルールを適用 → 出力
2行目 → sed が読み込み → 置換ルールを適用 → 出力
3行目 → sed が読み込み → 置換ルールを適用 → 出力
...
sed のフラグとオプション
g フラグ(すべて置換)
デフォルトでは、各行の最初のマッチだけを置換します。g フラグをつけると、その行のすべてのマッチを置換します。
文字列内の最初の「イヌ」を「トリ」に置換する
echo "イヌ ネコ イヌ" | sed "s/イヌ/トリ/"
# 出力: トリ ネコ イヌ
文字列内の全ての「イヌ」を「トリ」に置換する
echo "イヌ ネコ イヌ" | sed "s/イヌ/トリ/g"
# 出力: トリ ネコ トリ
-e オプション(複数の置換)
-e オプションを使用して、複数の置換を1つの sed コマンドにまとめられます。
file.txt内の「イヌ」を「ネコ」に、「トリ」を「サル」に置換する
sed -e "s/イヌ/ネコ/" -e "s/トリ/サル/" file.txt
各 -e の置換ルールは、1行を読み込むたびに順番にすべて適用されます。
つまり、1行目に対して s/イヌ/ネコ/ を適用し、続けて s/トリ/サル/ を適用します。
AWS Batch ジョブ定義の構成
AWS Batch のジョブ定義 JSON の多くは環境を問わず変わらない値ですが、Docker イメージの URI(タグがビルドごとに変わるため)や AWS アカウント ID(環境ごとに異なり Secrets で管理するため)など、デプロイ時に確定する値が一部あります。
変動する値にはプレースホルダー(__XXX__)を入れておき、デプロイ時に sed で置き換えます。プレースホルダーの形式は {{XXX}}、@XXX@ など様々です。今回はチームで採用していた __XXX__ を使用しています。
以下が今回使用する定義ファイルです。
{
"containerProperties": {
"image": "__IMAGE__",
"jobRoleArn": "arn:aws:iam::__ACCOUNT_ID__:role/my-batch-job-role",
"executionRoleArn": "arn:aws:iam::__ACCOUNT_ID__:role/ecsTaskExecutionRole"
}
}
✍️ 補足
今回扱う値は、1行に1度しか出現しないため、gフラグを使用する必要はありません。
-
__IMAGE__: ファイル内に 1箇所 -
__ACCOUNT_ID__: ファイル内に 2箇所(それぞれ別の行)
ワークフローでの sed コマンド
区切り文字に # を使う理由
sed の置換コマンドでは、区切り文字として最も一般的な / の代わりに、バックスラッシュと改行を除く任意の1文字を区切り文字として使用できます。
以下はすべて同じ動作をします。
sed "s/イヌ/ネコ/"
sed "s#イヌ#ネコ#"
sed "s|イヌ|ネコ|"
sed "s@イヌ@ネコ@"
今回のジョブ定義ファイルの置換では、ECR の URI や IAM ARN など置換値に `/` が含まれています。 区切り文字に `/` を使うと、sed は値の中の `/` も区切り文字だと解釈してしまい、構文エラーになります。
エラーになる例:
sed "s/__IMAGE__/123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:abc123/"
成功する例:
sed "s#__IMAGE__#123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:abc123#"
ECR の URI や IAM ARN に使われる文字列には、# は含まれていないため、# を区切り文字にすることで、コマンドが正しく動作します。
✍️ 補足
区切り文字には、検索文字列と置換文字列の両方に出現しない文字を選びます。URL やファイルパスを扱うなら / 以外で、#、|、@ などを選択し、どの文字にするかはチームで統一すると良さそうです。
実際のコマンド
GitHub Actions のワークフローファイルで、ジョブ定義を登録するステップの例です。
- name: Register scraping job definition
env:
IMAGE: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
run: |
sed -e "s#__IMAGE__#$IMAGE#" \
-e "s#__ACCOUNT_ID__#${{ secrets.AWS_ACCOUNT_ID }}#" \
batch-def/job_def.json > job_def.json
aws batch register-job-definition \
--cli-input-json file://job_def.json
このステップでは以下のことをしています。
-
envで環境変数IMAGEに ECR イメージの URI を設定 -
sedでジョブ定義ファイルのプレースホルダー(__IMAGE__、__ACCOUNT_ID__)を実際の値に置換 - 置換結果を別ファイル(
job_def.json)に出力 -
aws batch register-job-definitionで AWS Batch にジョブ定義を登録
⚠️ sed で置換する際の注意点
sed の置換文字列では & や \ がメタ文字として特別な意味を持つため、これらを文字通りに使いたい場合はエスケープする必要があります。
& の挙動(マッチした文字列全体に展開される)
echo "イヌ" | sed "s#イヌ#ネコ&トリ#"
# 出力: ネコイヌトリ
# & が "イヌ"(マッチした文字列)に展開される
echo "イヌ" | sed "s#イヌ#ネコ\&トリ#"
# 出力: ネコ&トリ
# \& でエスケープすると文字通りの & になる
\ の挙動(エスケープシーケンスの開始)
echo "イヌ" | sed "s#イヌ#ネコ\tトリ#"
# 出力: ネコ トリ
# \t がタブとして解釈される
echo "イヌ" | sed "s#イヌ#ネコ\\tトリ#"
# 出力: ネコ\tトリ
# \\ でエスケープすると文字通りの \t になる
今回は、AWS アカウント ID は数字のみ、ECR URI にもメタ文字は含まれないため問題ありません。しかし、パスワードなど特殊文字を含みうる値を扱う場合は、sed ではなく、JSONを安全に扱える jq( https://jqlang.github.io/jq/ ) などのツールもあります。
まとめ
- sed は入力を1行ずつ処理する
- URL や ARN など
/を含む値を扱う場合は、#、|、@など別の区切り文字を使う - 特殊文字を含む値を扱う場合は、
jqなど別のツールも検討する
参考