web-dev-qa-db-ja.com

スクリプト自体内でシェルスクリプト全体の出力をリダイレクトするにはどうすればよいですか?

Bourne Shellスクリプトのすべての出力をどこかにリダイレクトすることはできますが、スクリプト自体の中にShellコマンドがありますか?

単一のコマンドの出力をリダイレクトするのは簡単ですが、もっとこのようなものが欲しいです:

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

意味:スクリプトが非対話形式で実行される場合(たとえば、cron)、すべての出力をファイルに保存します。シェルから対話的に実行する場合は、通常どおり出力を標準出力に送ります。

通常、FreeBSD定期ユーティリティによって実行されるスクリプトに対してこれを行いたいと思います。それは毎日の実行の一部であり、私は通常、毎日メールで見ることを気にしないので、私はそれを送信しません。ただし、この特定のスクリプト内の何かが失敗した場合、それは私にとって重要であり、日常業務のこの一部の出力をキャプチャして電子メールで送信できるようにしたいと思います。

更新:ジョシュアの答えはスポットオンですが、スクリプト全体のstdoutとstderrを保存および復元したかったので、次のようにします。

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4
175
Steve Madsen

更新された質問への対処。

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

中括弧 '{...}'は、I/Oリダイレクトの単位を提供します。中括弧は、コマンドが現れる可能性のある場所-単純に、行の先頭またはセミコロンの後に現れなければなりません。 (はい、それはより正確にすることができます;あなたがめたい場合は、私に知らせてください。

元のstdoutとstderrを示したリダイレクトで保持できるのは正しいことですが、通常は、上記のようにリダイレクトされたコードのスコープを設定すると、後でスクリプトを維持する必要がある人が何をしているのかを理解するのが簡単になります。

Bashマニュアルの関連セクションは、 グループ化コマンド および I/Oリダイレクト です。 POSIXシェル仕様の関連セクションは、 複合コマンド および I/Oリダイレクト です。 Bashにはいくつかの余分な表記がありますが、それ以外はPOSIXシェル仕様に似ています。

137

通常、これらのいずれかをスクリプトの上部またはその近くに配置します。コマンドラインを解析するスクリプトは、解析後にリダイレクトを行います。

ファイルに標準出力を送信する

exec > file

stderrを使用

exec > file                                                                      
exec 2>&1

ファイルにstdoutとstderrの両方を追加します

exec >> file
exec 2>&1

ジョナサンレフラー彼のコメントに記載

execには2つの個別のジョブがあります。 1つ目は、現在実行中のシェル(スクリプト)を新しいプログラムに置き換えることです。もう1つは、現在のシェルのI/Oリダイレクトを変更することです。これは、execへの引数がないことで区別されます。

155
Joshua

スクリプト全体を次のような関数にすることができます。

main_function() {
  do_things_here
}

次に、スクリプトの最後にこれがあります:

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

または、実行するたびにすべてをログファイルに記録し、それを標準出力に出力するだけです:

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log
28
dbguy

元のstdoutとstderrを保存するには、次を使用できます。

exec [fd number]<&1 
exec [fd number]<&2

たとえば、次のコードは、「walla1」と「walla2」をログファイル(a.txt)に、「walla3」をstdoutに、「walla4」をstderrに出力します。

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6
6
Eyal leshem
[ -t <&0 ] || exec >> test.log
3
Dimitar