標準エラー出力をbash変数にリダイレクトする

標準エラー出力をbash変数にリダイレクトする

以下は、次の結果をもたらすCコードの断片ですsegfault

// segfault.c

#include <string.h>

int main()
{
    memset((char *)0x0, 1, 100);
    return 1;
}

コンパイル:

gcc segfault.c -o segfault

Bashで実行している場合:

$ ./segfault
Segmentation fault (core dumped)

これでb​​ashスクリプトで呼び出しをラップします。連続で3回試してみました。変数の中からエラー出力を取得しretて表示したいです。

#!/bin/bash

# segfault.sh

ret=`./segfault 2>&1`
echo "1) " $ret
ret=`eval ./segfault 2>&1`
echo "2) " $ret
ret=`eval ./segfault 2>&1 | cat`
echo "3) " $ret

Bashでスクリプトを実行する場合:

1) 
2) 
3)  ./segfault.sh: line 7: 28814 Segmentation fault (core dumped) ./segfault

明らかに、3番目の呼び出し形式のみが機能します。私の質問は、最初の2つのフォームがエラー出力をキャプチャできないのはなぜですか?

答え1

単純化されたbashスクリプトでのみ機能します(のみstderr)。

$ cat seg.sh 
#!/bin/bash
echo "Segfault" 1>&2
$ test=`./seg.sh`; echo "x$test"
Segfault
x
$ test=`./seg.sh 2>&1`; echo "x$test"
xSegfault
$ test=`eval ./seg.sh 2>&1`; echo "x$test"
xSegfault

あなたの場合、問題はSegmentation fault (core dumped)プログラムがあなたのプログラムによって書かれているのではなく(カーネルによって終了されるため)、子プロセスの終了に関する情報を取得する親プロセスによって書かれているという事実によって引き起こされます。cat前の例では、この効果を別のプロセスに配置してパイピングして非表示にしました。終了コードと以下を使用する必要がありますstderr

$ ./segfault; echo $?
Segmentation fault (core dumped)
139

答え2

「Segmentation failure(core dumped)」というメッセージは、競合するプログラムではなくbashによって表示されます(このメッセージが表示されたときにプログラムはすでに終了しています!)。リダイレクトはプログラム自体にのみ適用されます。

シェル自体からプログラムへのメッセージをリダイレクトするには、シェルグループ構成内でプログラムを実行し、グループ全体の出力をリダイレクトします。最も基本的なシェルグループ化構成は、グループ化のみを実行する中括弧です。

ret=`{ ./segfault; } 2>&1`

このフォームはコマンドの完全な評価ret=`eval ./segfault 2>&1`にリダイレクトを適用するため、原則として機能する必要があり、eval実際にはbash 4.3.30以前のシステムでも機能します。起こり得ること(kshで再現可能)は、bashバージョンがサブシェルの最後のコマンドであるときにサブルーチンをフォークしないようにいくつかの最適化を備えていることです。コマンドを実行する公称方法は次ret=`eval ./segfault`のとおりです。

  • パイプラインを作成します。
  • フォークはシェルサブプロセスを作成することを意味します。サブプロセス(プロセス1)から:
    • 出力をパイプにリダイレクトします。
    • eval組み込みを実行します。
    • クロス。サブプロセス(プロセス2)から:
      • 実装するfile./segfaultつまり、現在のプロセスで実行されているシェルプログラムをこのプログラムに置き換えますsegfault
    • (プロセス1から)プロセス2が完了するのを待ちます。
    • プロセス1は終了する。
  • (元のシェルプロセスで)パイプからデータを読み取り、データをret変数に蓄積します。
  • パイプが閉じると実行が続行されます。

ご覧のように、プロセス1は別のプロセスを作成してから完了するまで待ってからすぐに終了します。プロセス1が独自にリサイクルする方が効率的です。一部のシェル(およびシェルバージョン)は、この状況を認識して対応するために他のものよりも優れています。テールコールの最適化。ただし、プロセス2の場合、ret=`{ ./segfault; } 2>&1`プロセス2は標準エラーをファイル記述子1にリダイレクトしますが、プロセス1はそうではありません。試しているシェルバージョンでは、オプティマイザはこのケースを認識しません(おそらくtail callを実行しますが、リダイレクトを異なる設定にする必要があります)。

関連情報