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


49

私はこれを行うのに慣れてきました: someprogram >output.file

プログラムが生成した出力をファイルに保存するときはいつでも行います。また、このIOリダイレクションの 2つのバリアントも認識しています。

  • someprogram 2>output.of.stderr.file (stderrの場合)
  • 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
user @ hostname:〜$ 

この出力は、通常のようにファイルに保存できると期待していましたxinput test 10 > output.file。しかし、私の期待に反する場合、ファイルoutput.fileは空のままです。これはxinput test 10 &> output.file、stdoutまたはstderrで何かを見逃さないようにするためにも当てはまります。

私は本当に混乱しているので、xinputプログラムに出力がリダイレクトされるのを避ける方法があるかどうかをここで尋ねますか?

更新

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

//test.cファイル内

static void print_events(表示* dpy)
{
    XEventイベント。

    while(1){
    XNextEvent(dpy、&Event);

    // [...他のいくつかのイベントタイプがここで省略されます...]

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

        printf( "key%s%d"、(Event.type == key_release_type)? "release": "press"、key-> keycode);

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

ソースをこれに変更しました(次のスニペットを参照)。これにより、出力のコピーをstderrにコピーできます。この出力はリダイレクトできます:

 //test.cファイル内

static void print_events(表示* dpy)
{
    XEventイベント。

    while(1){
    XNextEvent(dpy、&Event);

    // [...他のいくつかのイベントタイプがここで省略されます...]

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

        printf( "key%s%d"、(Event.type == key_release_type)? "release": "press"、key-> keycode);
        fprintf(stderr、 "key%s%d"、(Event.type == key_release_type)? "release": "press"、key-> keycode);

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

現時点での私の考えは、リダイレクトを行うことにより、プログラムがキーを押すキーリリースイベントを監視する能力を失う可能性があることです。

回答:


55

stdoutが端末ではない場合、出力はバッファリングされるだけです。

そして、を押すとCtrl-C、そのバッファは、まだ書き込まれていない場合は失われます。

を使用して、何でも同じ動作をしstdioます。たとえば、試してください:

grep . > file

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

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

xinput test 10 > file

キーボードが十分に入力してバッファーがいっぱいになる(少なくとも4k相当の出力)と、一度に4kのチャンクでファイルのサイズが大きくなります

を使用するとgrep、バッファをフラッシュした後にCtrl-Dfor grepを入力して正常に終了できます。のためにxinput、私はそのようなオプションがあるとは思わない。

デフォルトstderrではバッファリングされていないことに注意してください。fprintf(stderr)

で、場合xinput.c、追加signal(SIGINT, exit)、教えていることxinput、それが受信した正常時に終了するには、SIGINTあなたは見ていないだろう、file安全が保証されていないシグナルハンドラからのライブラリの関数を呼び出すよう(、それがクラッシュしないと仮定すると、もはや空である:何を考えますprintfがバッファに書き込んでいる間に信号が入ると発生する可能性があります)。

使用可能な場合は、stdbufコマンドを使用してstdioバッファリング動作を変更できます。

stdbuf -oL xinput test 10 > file

このサイトにはstdio型のバッファリングを無効にすることをカバーする多くの質問があり、さらに多くの代替ソリューションが見つかります。


2
うわー:)それはトリックをしました。ありがとうございました。結局、問題に対する私の認識は間違っていました。リダイレクトを禁止する場所は何もありませんでした。データがフラッシュされる前にCtrl-Cで停止するだけでした。ありがとう
humanityANDpeace

標準出力のバッファリングを防ぐ方法はありますか?
humanityANDpeace

1
@Stephane Chazelas:詳細な説明をありがとう。あなたがすでに言ったことに加えて、私はバッファをバッファなしに設定できることを発見しましたsetvbuf(stdout, (char *) NULL, _IONBF, NULL)。たぶんこれも興味深い!?
user1146332

4
@ user1146332、はい、そうなりますが、出力が端末に送られるときのようにバッファリングstdbuf -o0stdbug -oL復元します。トリックを使用してアプリケーションを強制的に呼び出します。stdbufsetvbufLD_PRELOAD
ステファンシャゼル

別のworkaroudn: unbuffer test 10 > fileunbufferの一部であるexpectツール)
オリヴィエ・デュラック

23

コマンドは、/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

あなたの例では、ポイント+が質問に答えます。はい、可能です。もちろん、プログラムがそうするのは「予期しない」ことであり、非推奨です。少なくとも、そのようなことを可能にしようとは考えていませんでした。user1146332による回答も、リダイレクトを回避する説得力のある方法のようです。公平であり、与えられた両方の答えがコマンドラインプログラムの出力をファイルにリダイレクトすることを避けるための等しく可能な方法であるため、私は推測する答えを選択することはできません:(。ありがとう!
humanityANDpeace

1
FTR。Linux /dev/ttyシステムに書き込まれた出力をキャプチャする場合は、script -c ./demo demo.log(fromからutil-linux)を使用します。
ndim

ttyではなくptyで実行している場合は、procfs(/ proc / $ PID / fd / 0など)を見るとわかります。適切なptyに書き込むには、親プロセスのfdディレクトリに移動し、/ dev / pts / [0-9] +へのシンボリックリンクかどうかを確認します。次に、そのデバイスに書き込みます(または、ptsでない場合は再帰します)。
ダースナン

9

xinputファイルへの出力を拒否するように見えますが、端末への出力は拒否しません。これを実現するには、おそらくxinputシステムコールを使用してください

int isatty(int fd)

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

私は少し前に呼ばれるプログラムで同じ現象につまずいたdpic。ソースを調べてデバッグした後、関連する行を削除しisatty、すべてが期待どおりに機能しました。

しかし、この経験は非常に邪魔だということに同意します;)


私は本当に自分の説明があると思った。ただし、(1)ソース(xinputソースパッケージ内のtest.cファイル)を見ると、isattyテストが行​​われているようには見えません。出力は、printf関数(私はそれが標準Cだと思う)によって生成されます。私はいくつかを追加しましたがfprintf(stderr,"output")、これはリダイレクトすることが可能であり、xinputの場合にはコード全体が実際に実行されていることを証明します。ここでの最初のトレイルだったので、提案をありがとう。
humanityANDpeace

0

あなたにはtest.c、ファイルあなたが使用してバッファされたデータをフラッシュすることができ(void)fflush(stdout);、あなたの後に直接printf文。

    // in test.c
    printf("key %s %d ", (Event.type == key_release_type) ? "release" : "press  ", key->keycode);
    //fprintf(stderr,"key %s %d ", (Event.type == key_release_type) ? "release" : "press  ", key->keycode);
    //(void)fflush(NULL);
    (void)fflush(stdout);

コマンドラインで、コマンドを使用xinput test 10して擬似端末(pty)で実行することにより、行バッファー出力を有効にできますscript

script -q /dev/null xinput test 10 > file      # FreeBSD, Mac OS X
script -c "xinput test 10" /dev/null > file    # Linux

-1

はい。パスカルでプログラムしたときに、DOSでさえこれを行いました。私は原則がまだ当てはまると思います:

  1. 標準出力を閉じる
  2. コンソールとして標準出力を再度開きます
  3. 出力を標準出力に書き込む

これによりパイプが破損しました。


「stdoutを再度開く」:stdoutはファイル記述子1として定義されます。ファイル記述子1を再度開くことができますが、どのファイルを開きますか?おそらく、プログラムが1 fdに書き込みされているかどうか、それは重要ではありません、その場合には、端末を開いた意味
ジル「SO-停止されて悪」

@Gillesファイルは「con:」でした。覚えている限りですが、はい、その方向でポイント2を調整しました。
ニルス

conunixが呼び出すもののDOS名/dev/tty、つまり(制御)端末です。
ジル「SO-停止されて悪」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.