🐈

sedとteeコマンドで起きる謎の挙動と解決策

2022/05/19に公開

現象

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が空のファイルになるという謎の現象が起きた。

原因

こちらの記事が大変参考になった。
https://nebuta.hatenablog.jp/entry/2014/07/04/211728

原因は以下のように推測される:

  • 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