web-dev-qa-db-ja.com

標準出力を変数にキャプチャしますが、コンソールに表示します

いくつかの長時間実行プロセスを呼び出すbashスクリプトがあります。処理上の理由から、これらの呼び出しの出力を変数にキャプチャします。ただし、これらは長時間実行されるプロセスであるため、rsync呼び出しの出力をreal-timeでコンソールに表示し、事実。

この目的のために、私は found を行う方法を持っていますが、テキストを/ dev/stderrに出力することに依存しています。/dev/stderrへの出力は物事を行う良い方法ではないと感じています。

VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee /dev/stderr)

VAR2=$(rsync -r -t --out-format='%n%L' --delete -s /path/source1/ /path/target1 | tee /dev/stderr)

VAR3=$(rsync -r -t --out-format='%n%L' --delete -s /path/source2/ /path/target2 | tee /dev/stderr)

上記の例では、rsyncを数回呼び出しており、処理中にファイル名を確認したいのですが、後で解析するため、最終的には変数の出力が必要です。

これを達成するための「よりクリーンな」方法はありますか?

違いがある場合は、Ubuntu 12.04、bash 4.2.24を使用しています。

65
Mendhak

シェルで&1を複製し(私の例では5)、サブシェルで&5を使用します(親シェルのstdout(&1)に書き込みます)。

exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF

Aaaを2回出力します。1回はサブシェルのエコーのためで、2回目は変数の値を出力します。

あなたのコードで:

exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1
73
Op De Cirkel

Op De Cirkelの答えには正しい考えがあります。さらに簡単にできます(catの使用を回避):

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF
34
Russell Davis

stderrとコマンドの終了コードの両方をキャプチャする例を次に示します。これは、ラッセル・デイビスによる答えに基づいています。

exec 5>&1
FF=$(ls /taco/ 2>&1 |tee /dev/fd/5; exit ${PIPESTATUS[0]})
exit_code=$?
echo "$FF"
echo "Exit Code: $exit_code"

フォルダ/taco/が存在する場合、これはその内容をキャプチャします。フォルダーが存在しない場合、エラーメッセージがキャプチャされ、終了コードは2になります。

2>&1を省略すると、stdoutのみがキャプチャされます。

12
Bryan Roach

3つ以上のファイル記述子を使用できます。ここで試してください:

http://tldp.org/LDP/abs/html/io-redirection.html

「開いている各ファイルには、ファイル記述子が割り当てられます。[2] stdin、stdout、およびstderrのファイル記述子は、それぞれ0、1、および2です。追加ファイルを開く場合、記述子3〜9が残ります。これらの追加ファイル記述子の1つを、一時的な複製リンクとしてstdin、stdout、またはstderrに割り当てます。」

ポイントは、この結果を達成するためだけにスクリプトをより複雑にする価値があるかどうかです。実際、それはあなたがそれをする方法で、本当に間違っていません。

5
Piotr Wadas

「コンソール」が現在のTTYを意味する場合は、試してください

variable=$(command with options | tee /dev/tty)

TTYがない場合(cronジョブなど)、出力が予期しない場所に到達すると、これを使用しようとする人がときどき驚かされるため、これは少し疑わしい習慣です。

5
tripleee

/dev/tty、または他の回答で提案されている追加のファイル記述子を使用する代わりに、それを反転して一時ファイルを使用することもできます。これは間違いなく読みやすく、特定の状況でよりポータブルです。

tmpFile=$(mktemp)  # mak-a de temp
rsync /a /b | tee $tmpFile # sync my b*tch up
if grep "U F'd up" $tmpFile; then
  rm -rf / #Seppuku
fi
0
leondepeon