Perpipaan STDERR vs. STDOUT

24

Menurut " Linux: The Complete Reference 6th Edition " (hal 44), Anda hanya dapat mem- pipe STDERR menggunakan |&simbol pengalihan.

Saya telah menulis skrip yang cukup sederhana untuk menguji ini:

#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2

Saya menjalankan skrip ini seperti ini:

./script.sh |& sed 's:^:\t:'

Agaknya, hanya garis yang dicetak ke STDERR yang akan diindentasi. Namun, sebenarnya tidak berfungsi seperti ini, seperti yang saya lihat:

    Normal Text.
    Error Text. 

Apa yang saya lakukan salah di sini?

Naftuli Kay
sumber

Jawaban:

26

Saya tidak tahu teks apa yang digunakan buku Anda, tetapi manual bash jelas (jika Anda sudah terbiasa dengan pengalihan):

Jika |&digunakan, kesalahan standar command1, selain output standarnya, terhubung ke input standar command2 melalui pipa; itu adalah singkatan 2>&1 |. Pengalihan implisit kesalahan standar ke output standar dilakukan setelah setiap pengalihan ditentukan oleh perintah.

Jadi jika Anda tidak ingin mencampur output standar dan kesalahan standar, Anda harus mengarahkan output standar di tempat lain. Lihat Bagaimana cara menerima aliran kesalahan standar (stderr)?

{ ./script.sh 2>&1 >&3 | sed 's:^:\t:'; } 3>&1

Namun fd 1 dan 3 script.shdan sedakan menunjuk ke tujuan stdout asli. Jika Anda ingin menjadi warga negara yang baik, Anda dapat menutup fd 3 yang tidak diperlukan perintah-perintah itu:

{ ./script.sh 2>&1 >&3 3>&- | sed 's:^:\t:' 3>&-; } 3>&1

bashdan ksh93dapat menyingkat >&3 3>&-menjadi >&3-(fd move).

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Manual bash tidak sepenuhnya jelas bagi saya. Saya tidak mengerti mengapa sesuatu dgn seperti ./script.sh > /tmp/stdout_goes_here |& grep 'grepping_script_stderr'tidak bekerja sebagaimana dimaksud, yaitu: redirect script.sh's stdout(yang menurut potongan pengguna harus terjadi dahulu), kemudian memungkinkan grepuntuk memproses script stderr. Sebaliknya, stderrdan tdout` keduanya berakhir distdout_goes_here
sxc731
1
@ sxc731 |&adalah singkatan untuk 2>&1 |. Jadi >/tmp/stdout_goes_here |&pengalihan stdout ke /tmp/stdout_goes_here, lalu 2>&1pengalihan stderr ke mana pun stdout pergi, yaitu /tmp/stdout_goes_here, dan akhirnya |tidak menerima input apa pun karena output dari perintah telah diarahkan. Ingatlah bahwa >&1pengalihan ke mana pun file deskriptor 1 sedang berjalan , bukan ke mana pun deskriptor file 1 akan berakhir . Untuk mem-pipe stderr saja dan mengarahkan stdout ke file, salah satu caranya adalah 2>&1 >/tmp/stdout_goes_here |.
Gilles 'SANGAT berhenti menjadi jahat'
6

|&pipa stderr ke stdin, suka 2>&1 |, jadi program selanjutnya akan mendapatkan keduanya di stdin.

$cat test.sh
#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2
$./test.sh | sed 's:^:\t:'
Error Text.
        Normal Text.
$ ./test.sh |& sed 's:^:\t:'
        Normal Text.
        Error Text.
Kevin
sumber
Oh, jadi pada dasarnya setara dengan ekspresi yang lebih panjang runcommand 2>&1 | tee? yaitu runcommand |& tee?
Naftuli Kay
ya, mereka sama.
Kevin
1

|&di bash hanyalah jalan pintas (tidak terlalu portabel) untuk 2>&1 |, jadi Anda akan melihat setiap baris diindentasi.

jw013
sumber