リモート開発メインのソフトウェア開発企業のエンジニアブログです

docker run/exec 時にキャリッジリターンが混じる件の解決方法

小ネタです。実際には私の実行オプションの指定に問題があっただけなのですが、同じ様な事象で困ってる人に届く様、タイトルはこのままにしておきます。

結論

docker run or docker exec の結果をパイプしたりして他のプログラムに読ませる場合は不必要に -t (--tty) オプションはつけない様にしましょう。以下が、標準入力への入力の必要がないコマンドの正しい実行例の1つになります:

$ docker run --rm ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]"
2f33775

以下、経緯(蛇足)

コンテナが普及してから久しく、大抵の環境では Docker を筆頭にコンテナを起動できる様になり、ちょっとしたシェルスクリプトを書く場合にも docker コマンドを使う事が増えてきた様に思います。

この時もいつも通り使い捨てのシェルスクリプトを書いていました。内容は、実行する度にランダムな hex 文字列の先頭7文字を生成して、 Docker イメージにタグを付けると言った物でした。

最終的にはランダムな文字列ではなく git log -1 --format=%h の結果が入るのですが、まずは検証の段階なので、同じフォーマットにはしつつランダムな文字列を使いたかったのです。

bash ではこの様な文字列を生成するのが大変そうだったので、 docker run ruby で簡単な Ruby スクリプトを走らせて結果を得る事にしました:

hash=$(docker run --rm -it ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]")
docker build -t my-image:$hash .

このスクリプトを実行すると、以下のようなエラーになります:

invalid argument "my-image:ccdf908\r" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.

タグに使えない文字列であるキャリッジリターンが末尾に含まれています。これは、 hash 変数に代入した docker run の結果にキャリッジリターンが含まれているからです。od -c を使って見てみましょう:

$ docker run --rm -it ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]" | od -c
0000000    c   2   9   2   2   0   8  \r  \n
0000011

確かに改行コードの前にキャリッジリターンが含まれています。

キャリッジリターンを消す安直な方法

tr -d '\r' にパイプすると消せます。

hash=$(docker run --rm -it ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]" | tr -d '\r')
docker build -t my-image:$hash .

実際にこれで動作するし、一時的なコードだからこれで良かったのですが、腑に落ちなかったので検索エンジンで情報収集していたところ、 Docker 本体のリポジトリで以下の Issue コメントにたどり着きました:

Looks like this is indeed TTY by default translates newlines to CRLF

docker outputs to standard out with added carriage return character

tty はデフォルトで改行コードを CRLF に変換するみたいです。 tty の事や、何故この様な挙動になるかはまた今度別の記事に書くとして、今回はただ Ruby スクリプトの実行結果を取得したいだけなので tty は不要です。なので、 docker run の -t オプションを外しました。また同様の理由で -i も不要なので、最終的には次の様になります:

hash=$(docker run --rm ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]")
docker build -t my-image:$hash .

od -c の内容もバッチリです:

$ docker run --rm ruby ruby -e "require 'securerandom'; puts SecureRandom.hex[0,7]" | od -c
0000000    c   4   e   b   b   c   f  \n
0000010

これで無事動作しました。 docker run --rm -it は手癖になっていて何も考えず毎回こうしていたのですぐ気づきませんでした・・・。
因みに docker exec でも同様なのと、 docker compose はデフォルトで tty が有効になっているので、無効にする場合は -T 付ける必要がある点に注意が必要です:

docker compose run -T ...

本編よりだいぶ長くなりましたが、以上が経緯です。

← 前の投稿

DynamoDB でロックを実装する

次の投稿 →

[Rails] ActiveRecord のスコープはレコード作成時に値をセットしてくれる

コメントを残す