コマンドラインプログラムが出力がリダイレクトされるのを防ぐことはできますか?

コマンドラインプログラムが出力がリダイレクトされるのを防ぐことはできますか?

私はこれをするのに慣れています。 someprogram >output.file

私はプログラムで生成された出力をファイルに保存したいときはいつでもこれを行います。私もこれの2つのバリエーションを知っていますIOリダイレクト:

  • someprogram 2>output.of.stderr.file (標準誤差の場合)
  • someprogram &>output.stderr.and.stdout.file(stdout + stderrの組み合わせの場合)

今日は不可能だと思った状況に直面しました。次のコマンドを使用すると、期待xinput test 10どおりに次の結果が表示されます。

user@hostname:~$xinput テスト 10
ボタン30
リリースキー30
ボタン40
キーリリース40
ボタン32
キーリリース32
ボタン 65
キーリリース65
ボタン61
キーリリース61
ボタン 31
^C
ユーザー@ホスト名:~$

私はこの出力がいつものようにファイルに保存されることを期待しました。たとえば、.を使用しましたが、xinput test 10 > output.file私の期待とは異なり、出力.ファイルファイルは空でした。これはまた、xinput test 10 &> output.filestdoutやstderrで何かを見逃さないようにするためです。

本当に混乱しています。xinput出力がリダイレクトされるのを防ぐ方法があるかどうか、ここでプログラムに問い合わせてみましょう。

修正する

ソースコードを見ました。このコードによって出力が生成されたようです(下記のコードスニペットを参照)。出力は通常のprintfによって生成されるようです。

//test.c ファイルから

静的無効 print_events(*dpy 表示)
{
    XEventイベント;

    そして(1){
    XNextEvent(dpy、&イベント);

    // [...いくつかの異なるイベントタイプがここにリストされています...]

        if((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        整数ループ;
        XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event;

        printf("key %s %d ", (Event.type == key_release_type) ? "release" : "押された", key->keycode);

        for(loops=0;loopaxes_count;loop++){
        printf("a[%d]=%d ", key->first_axis + loop, key->axis_data[loop]);
        }
        printf("\n");
    }
    }
}

stderrから出力コピーを取得できるように、ソースコードをこれ(以下の次のコードスニペットを参照)に変更しました。この出力をリダイレクトできます。

//test.c ファイルから

静的無効 print_events(*dpy 表示)
{
    XEventイベント;

    そして(1){
    XNextEvent(dpy、&イベント);

    // [...いくつかの異なるイベントタイプがここにリストされています...]

        if((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        整数ループ;
        XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event;

        printf("key %s %d ", (Event.type == key_release_type) ? "release" : "押された", key->keycode);
        fprintf(stderr,"key %s %d ", (Event.type == key_release_type) ? "release" : "press ", key->keycode);

        for(loops=0;loopaxes_count;loop++){
        printf("a[%d]=%d ", key->first_axis + loop, key->axis_data[loop]);
        }
        printf("\n");
    }
    }
}

現在の私の考えは、リダイレクトを実行すると、プログラムがメジャーリリースイベントを監視する機能を失う可能性があることです。

答え1

stdout が端末でない場合にのみ出力がバッファリングされます。

を押すと、Ctrl-Cそのバッファがまだ書き込まれていないため失われます。

何でも使用すると同じ動作が得られますstdio。たとえば、次のようになります。

grep . > file

空でない行を入力してキーを押すと、Ctrl-Cファイルが空であることがわかります。

一方、次のように入力します。

xinput test 10 > file

その後、キーボードで十分に入力してください。バッファー全体(最小4k出力)を取得すると表示できます。文書一度に4kずつ増加します。

を使用すると、forと入力してバッファをフラッシュして正常にシャットダウンgrepできます。の場合にはそんなオプションがないと思います。Ctrl-Dgrepxinput

バッファリングはstderrデフォルトでは行われないので、他の動作が発生する理由を説明します。fprintf(stderr)

xinput.c1つを追加すると、つまり受信時に正常に終了するように指示するsignal(SIGINT, exit)と、もはやnullではないことがわかります(信号ハンドラでライブラリ関数を呼び出すことが安全であると保証されないため、競合しないと仮定)。 printfがバッファに書き込むかどうかを検討してください。これは、信号が領域に入ると発生します。xinputSIGINTfile

可能であれば、次のstdbufコマンドを使用してバッファリング動作を変更できますstdio

stdbuf -oL xinput test 10 > file

持つこのサイトに質問がたくさんあります。このオーバーライドは無効になります。標準入出力バッファリングを入力すると、より多くの代替ソリューションを見つけることができます。

答え2

/dev/tty一般的なリダイレクトが発生しないようにコマンドを直接作成できます。

$ cat demo
#!/bin/ksh
LC_ALL=C TZ=Z date > /dev/tty
$ ./demo >demo.out 2>demo.err
Fri Dec 28 10:31:57  2012
$ ls -l demo*
-rwxr-xr-x 1 jlliagre jlliagre 41 2012-12-28 11:31 demo
-rw-r--r-- 1 jlliagre jlliagre  0 2012-12-28 11:31 demo.err
-rw-r--r-- 1 jlliagre jlliagre  0 2012-12-28 11:31 demo.out

答え3

xinputファイル出力は拒否されますが、端末出力は拒否されているようです。これを達成するために、xinputシステムコールを使用できます。

int isatty(int fd)

開こうとしているファイル記述子が端末を参照していることを確認します。

しばらく前に私はというプログラムで同じ現象を偶然発見しましたdpic。ソースコードを見てデバッグした後、関連する行を削除し、isattyすべてが再び期待どおりに機能しました。

しかし、私はあなたの言葉に同意します。この経験は非常に衝撃的です。 ;)

答え4

はい。私はパスカルでプログラミングしていたDOS時代にもこのようなことをしました。私は次の原則がまだ有効だと思います。

  1. 標準出力を閉じる
  2. コンソールで標準出力を再度開く
  3. 標準出力に出力を書き込む

これは実際にすべてのパイプラインを中断します。

関連情報