次のコードがあります。
#!/bin/bash
VAR=0
func() {
VAR=$((VAR+1))
echo 'Logging information.'
}
func 2>&1 | tee 'log.txt'
echo "Should be 1: ${VAR}"
呼び出されると、次のことが発生します。
:~$ ./script.sh
Should be 1: 0
私が理解したのは、私が使用しているパイプラインがサブシェルを生成するためです。 in への変更はVAR
上向きに伝播されないため、出力に反映されません。
func
私の場合、プロセスはやや長く、出力はリアルタイムで行わなければなりませんでした。したがって、ファイルに書き込んでからcat
そのファイルを読むことはオプションではありません。また、可能であればファイルに何も書かずに変数で読み直すことを避けたいと思います。
それでは、パイプの必要性を満たす方法はありますか?それとも私がまだ知らないのに役立つbashトリックはありますか?
編集する:
名前付きパイプを試してください。
#!/bin/bash
VAR=0
func() {
VAR=$((VAR+1))
echo 'Logging information.'
sleep 5
}
mkfifo my_fifo
func >my_fifo 2>&1 &
tee 'log .txt' <my_fifo
echo "Should be 1: ${VAR}"
残念ながら、結果は同じです。
:~$ ./script.sh
Should be 1: 0
答え1
次のことができます。
func > >(tee log.txt) 2>&1
wait
ロギング専用のファイル記述子を指定できます。
exec 3> >(tee log.txt)
tee_pid=$!
func >&3 2>&1
...
ただし、tee
バックグラウンドで実行されるため、すべての出力が通過しないと、出力順序が影響を受ける可能性があります。
答え2
TMPファイルが利用可能
func >tmpfile 2>&1
tee 'log.txt' <tmpfile
または先入れ先出し
mkfifo pipe_replacement
tee 'log.txt' <pipe_replacement &
func >pipe_replacement 2>&1
答え3
私はこれをします...
#!/bin/sh -x
run() if ! ps -p "$run" >&2
then n=0 run=$$ exec "$0" "$@" 2>&1 | {
! tee outfile ; }
fi 2>/dev/null
run "$@" || exit
fn() { var=val$n; echo "$((n+=1)): $var"; }
fn
sleep 5
fn
IN
まず、他のプロセスのパイプを開いていることを確認してくださいtee
。そうでない場合は、exec
プロセス自体に入ります。その後、すべての出力はスクリプトの残りの部分にパイプされ、その間に設定されたすべての変数とスクリプト終了が保持されます。実際にこれが最初から確認される方法です。
したがって、上記の印刷を実行してください。
+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit
その後、印刷を実行しますcat outfile
...
+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit
パフォーマンスが高い場合でも、代わりsed
に使用することを検討することもできますtee
。tee
フル出力この場合、sed
複数のファイルを同時に書き込むことができ、条件に応じて実行できます。
たとえば、次の行をエコーできます。
echo 'LOG-ONLY: some message here'
リスニングsed
プロセスは次のことができます。
sed -n '/^LOG-ONLY:/!p;s///w ./my_log.txt'
...w
部分を削除した後、ファイルに行を書き込んでLOG_ONLY
端末に印刷するのを防ぎます。