実行可能なCプログラムのエラーメッセージをstdoutにリダイレクトする方法は? (Mac OS X)

実行可能なCプログラムのエラーメッセージをstdoutにリダイレクトする方法は? (Mac OS X)

自動Cプログラムチェッカーを書きたいです。たとえば、おもちゃ「hello.c」プログラムがあります。

#include <stdio.h>

int main()
{
    int a, b;

    while (scanf("%d %d", (&a)-1000000000000000, &b) != EOF)
    {
        printf("%d\n", a+b);
    }

    return 0;
}

これは私の入力ファイル「1.in」です。

1 2
4 5
10 10
2 2
3 2
7 4

出力ファイル「1.out」:

3
9
20
4
5
11

「gcc hello.c -o hello.o」を使用して、実行プログラム「hello.o」をコンパイルして生成します。明らかに、プログラムで「segfault」が発生しました。 (マイMAC OS Xで実行中)

$ ./hello.o <1.in
Segmentation fault: 11

しかし、パイプとの違いを使用して自動チェッカーを作成したいと思います。

./hello.o <1.in | diff - 1.out

出力は次のとおりです

0a1,6
> 3
> 9
> 20
> 4
> 5
> 11

エラーメッセージは表示されません!しかし、端末(MAC OS X)に表示したいと思います。

次のようにstderrをstdoutにリダイレクトしようとしました。

./hello.o <1.in 2>&1 | diff - 1.out

しかし、効果はありません!

また、stderrを次のファイルにリダイレクトしようとしました。

./hello.o <1.in 2>log

そして、「Segmentation failure:11」というメッセージが端末に表示されますが、ファイルには何もありません。

私が使うときも同じです。

./hello.o <1.in &>log

たぶんエラーメッセージがstderrにないかもしれません。

それでは、この問題をどのように解決するのですか?ありがとうございます!

ベストアンサー1

プログラムの標準エラーストリームを標準出力にリダイレクトしましたが、そのメッセージがプログラムによって発行されなかったため、「セグメントエラー」メッセージはリダイレクトされません。代わりに、プログラムを呼び出すシェルによって発行されます。

ここでやるべきことはあなたの状況によって異なります。実際目標は次のとおりです。

標準エラーを標準出力にリダイレクトする場合は、他のコマンドと同様にCプログラムを使用してこれを実行できます。このようなリダイレクト2>&1と使用する他の方法は正しく機能します。あなたのプログラムは実際には標準エラーに何も書いていませんが、Cプログラムを書いている場合するこれにより、リダイレクトされていることがわかります。fputsあるいは、関数を使って書くこともfprintfできますstderr。たとえば、次のようになります。

fprintf(stderr, "error: refusing to say hello world\n");

一方、SIGSEGVリダイレクトコマンドの作成を使用してサブプロセスを終了した後、シェルによって作成された「セグメントエラー」メッセージを標準エラーにリダイレクトすることが目的の場合。他のコマンドを使用する必要はありません。ザイルズは説明する、コマンドの1つを含めるだけで十分です{ ;}。たとえば、次のように実行できます。

{ ./hello.o 1000; } 2>>outfile

これは、プログラム実行のすべての標準エラー出力(プログラムで生成された出力(この場合はなし)とシェルで生成された出力を含む)をファイルに追加しますoutfile

(しかし私はあなたが本当に疑わしいです。するプログラムが実際に生成できるエラー出力をリダイレクトしたいので、重複した閉じてマークするのではなく、この質問に答えることです。 )


Cプログラムが明らかに偽のアドレスに書き込むようにしてください。〜らしい意図的に分割エラーを発生させることは信頼できる方法ですが、実際にはそうではありません。これは、Cコンパイラが未定義の動作を引き起こさないと仮定する可能性があり、一部のコンパイラは、高度な最適化なしでコンパイルするときにもこの仮定を利用するためです。分割エラーの下でプログラムの動作をテストするには、プログラムのSIGSEGV実行中にプログラムにシグナルを送信することをお勧めします。raiseこの機能を使用して自分でこれを実行することも、killコマンドkillallを使用して実行することもできます。

たとえば、上記の例をテストするために{ sleep 1000; } >&out別の端末で実行しましたkillall -SEGV sleep。 (このsleepコマンドはバックグラウンドで使用される可能性があるため、本番システムでは正確なプロセスを実行したくない可能性があることを警告する必要があります。少なくとも他の重要なタスクを実行しているユーザーではなく、もちろんrootユーザーになりたくありません。

最後に、実行可能ファイルの名前をサフィックスとして指定したくありません.o。これらのサフィックスは通常、実際に実行可能ファイルを生成するために互いにリンクする必要があるコンパイラ生成オブジェクトファイルに使用されるためです。 Unixファミリーオペレーティングシステムで実行可能なバイナリは通常、拡張子なしで名前が付けられています。

おすすめ記事