このBourneシェルスクリプトで `exec 2>&1`が失敗するのはなぜですか?

このBourneシェルスクリプトで `exec 2>&1`が失敗するのはなぜですか?

以前のkshスクリプトをBourneシェルに移植しています。古いkshスクリプトには次のコードが含まれています。

#!/bin/sh

tmpLog=/var/tmp/logfile.$$

exec 1> $tmpLog
exec 2>&1

eval $*
another_command_1
another_command_2

私が読んだところ、これら2つのexecステートメントは$ *、another_command_1、another_command_2、そして次のコマンドの両方を実行し、そのコマンドのすべてのSTDERRとSTDOUTを/var/tmp/logfile.$$。スクリプトこのスクリプトを使用している場合、スクリプトはにありますexec 2>&1

stefanl@host:~ $ sh -xv ./output.sh echo "Hello"
#!/bin/sh

tmpLog=/var/tmp/logfile.$$
+ tmpLog=/var/tmp/logfile.39918

exec 1> $tmpLog
+ exec
exec 2>&1
+ exec
stefanl@host:~ $

コマンドラインからこのコマンドを実行すると、実行後にシェルがフリーズしますexec 2>&1

stefanl@host:~ $ tmpLog=/var/tmp/logfile.$$
stefanl@host:~ $ exec 1> $tmpLog
stefanl@host:~ $ exec 2>&1
### FREEZE ###

私の質問:

  1. 何をすべきですかexec 2>&1
  2. なぜ私に失敗したのですか?

答え1

あなたのスクリプトは失敗しません。正常に動作します。exec >logfile; exec 2>&1標準出力と標準エラーlogfile。現在のシェルでこれらのリダイレクトを直接実行すると、端末からすべての出力が送信されたため、シェルがハングしているように見えます。

xtrace()オプションの出力はset -x常にファイルディスクリプタ2の標準エラーに移動することに注意してください。ログファイルに送信するファイル記述子です。そこで残りの内容を見つける必要がありますexec 2>&1

答え2

この形式のexec(つまり、コマンドなし)は、現在のシェルインタプリタからのすべての後続の出力をリダイレクトするために使用されます。

Bashの組み込みヘルプ:

$は実行を助けます
exec: exec [-cl] [-a name] [コマンド [パラメータ...]] [リダイレクト...]
    シェルを与えられたコマンドに置き換えます。

    COMMANDを実行して、このシェルを指定されたプログラムに置き換えます。
    ARGUMENTS は COMMAND のパラメーターになります。コマンドが指定されていない場合
    すべてのリダイレクトは現在シェルに適用されます。
    [...]

私はexec &> logfile stdoutとstderrを同時にリダイレクトするために使用します。たとえば、ほとんどのバックアップスクリプトとrsyncラッパースクリプト(または後で詳細に調べることができる多くの出力を生成するスクリプト)は、次のように開始されます。

BNAME=$(basename "$0" .sh)
LOGFILE="/tmp/$BNAME.log"
savelog "$LOGFILE"
exec &> "$LOGFILE"

次に、cronまたはバックグラウンドでスクリプトを実行し、tail -Fスクリプトの実行中にログファイルを監視します。ログ保存を使用すると、最後の7つの実行の出力を維持できます(デフォルトは7で、savelog -c変更に使用できます)。

関連情報