tellme.tokyo

標準出力に出しつつ、パイプ先のコマンドにも繋ぐ

コマンドの出力をターミナル (stdout) に出しながらパイプに繋いだ別のコマンドの stdin に流すとき、どう書くか。

結論

command1 | tee >(command2)
flowchart LR
  command1 -->|out| out[stdout]
  command1 -->|in| command2
  command2 -->|out| out

解説

tee コマンドを使うとできる。tee は標準入力から来たデータを標準出力とリダイレクト先にストリーム出力することができる。

そこでリダイレクト先をプロセス置換 (Process Substitution)1でパイプでつなぎたいコマンドを指定することで、標準出力に出しつつ特定のコマンドの入力に接続できる。

seq 15 | tee >(grep 4)
1      # seq
2      # |
3      # |
4      # |
5      # |
6      # |
7      # |
8      # |
9      # |
10     # |
11     # |
12     # |
13     # |
14     # |
15     # |
4      # grep
14     # |

標準出力ではなく標準エラー出力に出したい場合は次のようにする (やっていることは FD 1番を FD 2番に向けるだけ)。

seq 15 | tee >(grep 4) >&2
# Or
seq 15 | tee >&2 >(grep 4)

実際の利用事例として「何かしらのコマンドを実行し CI のコンソールにも出しつつ GitHub にもコメントにする」ときとかに便利。

date | tee >(gh issue comment --repo babarot/sandbox 12 --body-file -)

mercari/tfnotify も最初はこういう感じのシェルスクリプトから始まったことを思い出した。

notify() {
  local comment="$(tee >(cat) >&2)"
  local template="## Some results
\`\`\`
%s
\`\`\`
"

  comment="$(printf "${template}" "${comment}")"
  gh issue comment --repo babarot/sandbox 12 --body "${comment}"
}

do_something() { date; }

# main
do_something | notify
実行した様子

実行した様子