Docker コンテナの出力を jq にパイプしたらインデントが崩れるのを直す

docker rundocker exec を使って、AWS CLI 的な CLI ツールを動かす。実行結果は JSON 形式で返ってくるので、コレをパイプして jq で整形しようと思った。

# 「get status」的なサブコマンドを実行しているイメージ
$ docker run --rm -it my-cli-container get status | jq '.'
{
                "status": {
                                "name": "HOGE',
                                                "created": "2020-01-01",
                                                                "info": "Running"
                                                                                }
                                                                                                }

出力結果はイメージだが、なぜかこんな風に、どんどんインデントが増えていくような見た目になってしまった。

対策2つ

jq の GitHub Issues にドンズバの質問が挙がっていて、解決策も分かった。

まず、自分で導いた対処法は、Bash のコマンド置換 ($() やバッククォートでコマンドを囲むアレ) を使うモノ。

$ echo "$(docker run --rm -it my-cli-container get status)" | jq '.'

こんな風にすればインデントが崩れなくなった。

GitHub Issues で見つけた対処法は、moreutils の sponge コマンドを挟む、というモノ。

sponge コマンドは以前も紹介したが、標準入力を一旦蓄えて、標準出力に流せるコマンド。

コレを以下のように使う。

$ docker run --rm -it my-cli-container get status | sponge | jq '.'

根本原因は -t が余計だった

よくよく調べてみると、docker run-t オプションを付けてるのが悪いんや、という指摘が。

It's natural that we can't expect correct behavior with piping against tty output.
Don't use -t flag of docker when you use pipe from its output.
This behavior is not a bug of docker nor of jq.
The sponge from moreutils seems to solve the problem but I'm rather surprised that we can't see the tty output as well (like echo '{"a":1}\n{"b":2}\n' >/dev/tty | jq .).

意識せず使っていたが、-t オプションを外すだけでキレイに行けた。

# -t オプションを外す
$ docker run --rm -i my-cli-container get status | jq '.'

# なんなら -i オプションもなくて大丈夫だった
$ docker run --rm my-cli-container get status | jq '.'

マジか…。