Shell Script Advent Calendar 2015 7日目の記事です。 BashのちょっとしたTipsです。 Bashに限らないかも知れませんが、検証環境がBashしかありませんでした。
問題
Bashで $(command)
と書くと、command
がサブシェルで実行され、
stdout(標準出力)を変数に格納することができます.
hoge_value=$(echo 'hoge') # hoge_value => "hoge"
ただしstderr(標準エラー出力)はキャプチャできません。 いま、stdoutとstderrへ出力し、100を返す関数があるとします。
out_err_and_exit() { echo "This is stdout" echo "This \\is \"tricky 'stdout" echo "This is stderr" >&2 echo "This \\is \"tricky 'stderr" >&2 return 100 }
この関数を愚直に $(...)
で出力を受け取ろうとしても、取りこぼしたstderrが無残にもターミナルに表示されます。
stdout=$(out_err_and_exit)
それではstdout/stderr/exit code をそれぞれ $stdout
, $stderr
, $status
に格納するにはどうすればよいのでしょうか?
解答
. <( out_err_and_exit \ 2> >(stderr=$(</dev/stdin); declare -p stderr) \ 1> >(stdout=$(</dev/stdin); declare -p stdout) declare -i status=$? declare -p status )
以下のコードで、特殊文字も難なく表示できるのが確認できます
echo "stdout => $stdout" echo "stderr => $stderr" echo "status => $status"
解説
out_err_and_exit
のコマンドの実行結果を、>(...)
を使ってそれぞれサブシェルに投げています。
サブシェル内では受け取った出力が /dev/stdin
経由で参照できます。
なので /dev/stdin
を $(...)
で取得して、それを $stdout
, $stderr
変数に格納します。
しかしサブシェル内のコードはサブシェル外に副作用が無いので、サブシェル外からは $stdout
, $stderr
にアクセスできません。
そこで declare -p
の登場です。
組込みコマンド declare
に -p
オプションをつけると、evalできるフォーマットで変数の名前と値が表示されます。
declare -p SHELL # => declare -x SHELL="/bin/bash"
$stdout
, $stderr
を declare -p
して定義を出力し、
外側のシェルでその出力を評価すると、外部でも $stdout
, $stderr
が定義されます。
トップレベルのシェルでは、.
コマンド (= source
コマンド) に . <(...)
して出力を評価させます。
問題ではexit codeも$status
に格納するので、. <(...)
内で $?
を $status
に格納して、同様に declare -p
してます。
この記事は Shell Script Advent Calendar 2015 7日目の記事です。 明日は @yudsuzuk さんです。