Bisakah program baris perintah mencegah outputnya dialihkan?

49

Saya sudah terbiasa melakukan ini: someprogram >output.file

Saya melakukannya setiap kali saya ingin menyimpan output yang dihasilkan suatu program ke file. Saya juga mengetahui dua varian pengalihan IO ini :

  • someprogram 2>output.of.stderr.file (untuk stderr)
  • someprogram &>output.stderr.and.stdout.file (untuk kedua stdout + stderr digabungkan)

Hari ini saya telah mengalami situasi yang saya pikir tidak mungkin. Saya menggunakan perintah berikut xinput test 10dan seperti yang diharapkan, saya memiliki output sebagai berikut:

user @ hostname: ~ $ xinput test 10
tekan tombol 30 
pelepasan kunci 30 
tekan tombol 40 
pelepasan kunci 40 
tekan tombol 32 
pelepasan kunci 32 
tekan tombol 65 
pelepasan kunci 65 
tekan tombol 61 
pelepasan kunci 61 
tekan tombol 31 
^ C
user @ hostname: ~ $ 

Saya berharap bahwa output ini seperti biasa dapat disimpan ke file seperti menggunakan xinput test 10 > output.file. Tetapi ketika bertentangan dengan harapan saya file output.file tetap kosong. Ini juga berlaku untuk xinput test 10 &> output.filememastikan saya tidak melewatkan sesuatu di stdout atau stderr.

Saya benar-benar bingung dan karenanya bertanya di sini jika xinputprogram mungkin memiliki cara untuk menghindari outputnya untuk diarahkan?

memperbarui

Saya telah melihat sumbernya. Tampaknya output dihasilkan oleh kode ini (lihat cuplikan di bawah). Tampaknya bagi saya output akan dihasilkan oleh printf biasa

// dalam file test.c

void print_events statis (Tampilan * dpy)
{
    Acara XEvent;

    sementara (1) {
    XNextEvent (dpy, & Event);

    // [... beberapa jenis acara lainnya dihilangkan di sini ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        int loop;
        XDeviceKeyEvent * key = (XDeviceKeyEvent *) & Acara;

        printf ("key% s% d", (Event.type == key_release_type)? "rilis": "tekan", key-> kode kunci);

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

Saya memodifikasi sumber untuk ini (lihat potongan berikutnya di bawah), yang memungkinkan saya untuk memiliki salinan output di stderr. Output ini saya dapat mengarahkan:

 // dalam file test.c

void print_events statis (Tampilan * dpy)
{
    Acara XEvent;

    sementara (1) {
    XNextEvent (dpy, & Event);

    // [... beberapa jenis acara lainnya dihilangkan di sini ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        int loop;
        XDeviceKeyEvent * key = (XDeviceKeyEvent *) & Acara;

        printf ("key% s% d", (Event.type == key_release_type)? "rilis": "tekan", key-> kode kunci);
        fprintf (stderr, "key% s% d", (Event.type == key_release_type)? "rilis": "tekan", key-> keycode);

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

Gagasan saya saat ini adalah bahwa mungkin dengan melakukan pengalihan, program kehilangan kemampuannya untuk memantau acara-acara pelepasan kunci.

humanityANDpeace
sumber

Jawaban:

55

Hanya saja ketika stdout bukan terminal, output buffered.

Dan ketika Anda menekan Ctrl-C, buffer itu hilang sebagai / jika belum ditulis.

Anda mendapatkan perilaku yang sama dengan menggunakan sesuatu stdio. Coba misalnya:

grep . > file

Masukkan beberapa baris yang tidak kosong dan tekan Ctrl-C, dan Anda akan melihat file tersebut kosong.

Di sisi lain, ketik:

xinput test 10 > file

Dan cukup ketik pada keyboard untuk mendapatkan buffer penuh (setidaknya 4k senilai ouput), dan Anda akan melihat ukuran file tumbuh dengan potongan 4k sekaligus.

Dengan grep, Anda bisa mengetikkan Ctrl-Duntuk grepkeluar dengan anggun setelah mem-buffer-nya. Karena xinput, saya tidak berpikir ada opsi seperti itu.

Perhatikan bahwa secara default stderrtidak ada buffer yang menjelaskan mengapa Anda mendapatkan perilaku yang berbedafprintf(stderr)

Jika, dalam xinput.c, Anda menambahkan a signal(SIGINT, exit), yang mengatakan xinputuntuk keluar dengan anggun ketika diterima SIGINT, Anda akan melihat filetidak lagi kosong (dengan asumsi itu tidak mogok, karena fungsi perpustakaan panggilan dari penangan sinyal tidak dijamin aman: pertimbangkan apa dapat terjadi jika sinyal masuk saat printf menulis ke buffer).

Jika tersedia, Anda bisa menggunakan stdbufperintah untuk mengubah stdioperilaku buffering:

stdbuf -oL xinput test 10 > file

Ada banyak pertanyaan di situs ini yang mencakup menonaktifkan penyangga tipe stdio di mana Anda akan menemukan lebih banyak solusi alternatif.

Stéphane Chazelas
sumber
2
WOW :) itu berhasil. Terima kasih. Jadi pada akhirnya persepsi saya tentang masalah itu salah. Tidak ada yang menghalangi redirection, itu sederhana Ctrl-C menghentikannya sebelum data memerah. terima kasih
humanityANDpeace
Apakah akan ada cara untuk mencegah buffering stdout?
humanityANDpeace
1
@Stephane Chazelas: terima kasih banyak atas penjelasan terperinci Anda. Selain apa yang telah Anda katakan, saya menemukan bahwa seseorang dapat mengatur buffer untuk dihapus setvbuf(stdout, (char *) NULL, _IONBF, NULL). Mungkin ini juga menarik !?
user1146332
4
@ user1146332, ya, itu akan menjadi apa stdbuf -o0, sementara stdbug -oLmengembalikan garis buffering seperti ketika output masuk ke terminal. stdbuftidak memaksa aplikasi untuk memanggil setvbufmenggunakan LD_PRELOADtrik.
Stéphane Chazelas
workaroudn lain: unbuffer test 10 > file( unbufferadalah bagian dari expectalat)
Olivier Dulac
23

Sebuah perintah dapat langsung menulis untuk /dev/ttymencegah pengalihan rutin terjadi.

$ 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
Jlliagre
sumber
Contoh Anda membuat poin + menjawab pertanyaan. Ya itu mungkin. Tentu saja "tidak terduga" dan tidak perlu ada program untuk melakukannya, yang setidaknya membodohi saya karena tidak mempertimbangkan hal seperti itu mungkin. Jawaban oleh user1146332 juga tampaknya cara yang meyakinkan untuk menghindari pengalihan. Agar adil dan karena kedua jawaban yang diberikan adalah cara yang sama-sama mungkin untuk menghindari pengalihan output program baris perintah ke file, saya tidak dapat memilih salah satu jawaban yang saya kira :(. Saya harus diizinkan untuk memilih dua jawaban dengan benar. Kerja bagus, Terima kasih!
humanityANDpeace
1
FTR, jika Anda ingin menangkap keluaran yang ditulis /dev/ttydi sistem Linux, gunakan script -c ./demo demo.log(dari util-linux).
ndim
Jika Anda tidak menjalankan dalam tty, tetapi sebaliknya dalam pty, Anda dapat menemukannya dengan melihat procfs (/ proc / $ PID / fd / 0 dll). Untuk menulis ke pty yang sesuai, buka direktori proses orangtua Anda dan lihat apakah itu symlink ke / dev / pts / [0-9] +. Kemudian Anda menulis ke perangkat itu (atau berulang jika itu bukan Poin).
dhasenan
9

Sepertinya xinputmenolak output ke file tetapi tidak menolak output ke terminal. Untuk mencapai ini, mungkin xinputgunakan system call

int isatty(int fd)

untuk memeriksa apakah skrip yang diajukan dibuka merujuk ke terminal atau tidak.

Saya menemukan fenomena yang sama beberapa waktu lalu dengan sebuah program bernama dpic. Setelah saya melihat ke sumber dan beberapa debug saya menghapus baris yang berhubungan dengan isattydan semuanya berfungsi seperti yang diharapkan lagi.

Tapi saya setuju dengan Anda bahwa pengalaman ini sangat mengganggu;)

pengguna1146332
sumber
Saya benar-benar berpikir saya memiliki penjelajahan saya. Tapi (1) melihat sumber (file test.c dalam paket sumber xinput) tidak ada tampilan isattypengujian yang dilakukan. Ouput dihasilkan oleh printffungsi (saya pikir ini standar C). Saya menambahkan beberapa fprintf(stderr,"output")dan ini mungkin untuk mengarahkan + membuktikan seluruh kode benar-benar dijalankan dalam kasus xinput. Terima kasih atas sarannya setelah semua itu adalah jejak pertama di sini.
humanityANDpeace
0

Dalam test.cfile Anda, Anda dapat menyiram data yang disangga menggunakan (void)fflush(stdout);langsung setelah printfpernyataan Anda .

    // 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);

Pada baris perintah Anda dapat mengaktifkan output buffer-line dengan menjalankan xinput test 10terminal pseudo (pty) dengan scriptperintah.

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

Iya. Saya bahkan melakukan ini dalam DOS-kali ketika saya memprogram dalam pascal. Saya kira prinsipnya masih berlaku:

  1. Tutup stdout
  2. Buka kembali stdout sebagai konsol
  3. Tulis hasilnya ke stdout

Ini memang mematahkan pipa.

Nils
sumber
“Buka kembali stdout”: stdout didefinisikan sebagai file descriptor 1. Anda dapat membuka kembali file descriptor 1, tetapi file apa yang akan Anda buka? Anda mungkin bermaksud membuka terminal, dalam hal ini tidak masalah apakah program sedang menulis ke fd 1.
Gilles 'SO-stop evil'
@Gilles file itu "con:" sejauh yang saya ingat - tapi ya, saya memperbaiki titik 2 ke arah itu.
Nils
conadalah nama DOS untuk panggilan unix /dev/tty, yaitu terminal (pengontrol).
Gilles 'SANGAT berhenti menjadi jahat'