🐈
sedとteeコマンドで起きる謎の挙動と解決策
現象
Intel MacとM1 Mac (Arm64)両方に対応する目的で、docker-compose build
する前に下記のスクリプトを実行するようにMakefileを設定していた。
M1の場合にはdocker-compose-base.yaml
の内容を一部置換してdocker-compose.yaml
へ出力する、というシンプルな内容。
docker-arch.sh
arch_name="$(uname -m)"
file="docker-compose.yaml"
rm -f $file
cp docker-compose-base.yaml $file
if [ "${arch_name}" = "arm64" ]; then
sed 's/ mysql:/ mysql:\n platform: linux\/arm64\/v8/' ${file} | tee ${file}
sed 's/mysql:8.0/mysql:8.0-oracle/' ${file} | tee ${file}
fi
ところが、出力されるdocker-compose.yaml
が空のファイルになるという謎の現象が起きた。
原因
こちらの記事が大変参考になった。
原因は以下のように推測される:
-
sed
コマンドの実行が終わる前にパイプの先のコマンドtee
が実行される -
tee
は空の文字列をファイル出力(ファイルは空になる) -
sed
による置換をしようとするが入力は空なのでそのまま空文字がファイル出力(やはりファイルは空になる)
コピー元のdocker-compose-base.yaml
の内容が短い場合などは再現しないため、原因究明まで手こずった。
解決策
パイプを使用せずにsed
の-i
オプションでファイルに上書き(インプレイス)保存することで解決。
docker-arch.sh
- sed 's/ mysql:/ mysql:\n platform: linux\/arm64\/v8/' ${file} | tee ${file}
- sed 's/mysql:8.0/mysql:8.0-oracle/' ${file} | tee ${file}
+ sed -i '' 's/ mysql:/ mysql:\n platform: linux\/arm64\/v8/g' ${file}
+ sed -i '' 's/mysql:8.0/mysql:8.0-oracle/g' ${file}
Discussion