EDIT: Saya melihat saya tergelincir dan akhirnya menjawab pertanyaan yang berbeda dari yang ditanyakan. Jawaban atas pertanyaan sebenarnya ada di bagian bawah jawaban Paul Tomblin. (Jika Anda ingin meningkatkan solusi itu untuk mengalihkan stdout dan stderr secara terpisah karena alasan tertentu, Anda dapat menggunakan teknik yang saya jelaskan di sini.)
Saya menginginkan jawaban yang menjaga perbedaan antara stdout dan stderr. Sayangnya, semua jawaban yang diberikan sejauh ini yang mempertahankan perbedaan itu rentan terhadap ras: program berisiko melihat masukan yang tidak lengkap, seperti yang saya tunjukkan dalam komentar.
Saya pikir saya akhirnya menemukan jawaban yang mempertahankan perbedaan, tidak rawan ras, dan juga tidak terlalu fiddly.
Blok penyusun pertama: untuk menukar stdout dan stderr:
my_command 3>&1 1>&2 2>&3-
Blok penyusun kedua: jika kita ingin memfilter (misalnya tee) hanya stderr, kita dapat melakukannya dengan menukar stdout & stderr, memfilter, dan kemudian menukar kembali:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Sekarang sisanya mudah: kita bisa menambahkan filter stdout, baik di awal:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
atau di akhir:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Untuk meyakinkan diri sendiri bahwa kedua perintah di atas berfungsi, saya menggunakan yang berikut ini:
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
Outputnya adalah:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
dan perintah saya muncul segera setelah " teed stderr: to stderr
", seperti yang diharapkan.
Catatan kaki tentang zsh :
Solusi di atas berfungsi di bash (dan mungkin beberapa shell lain, saya tidak yakin), tetapi tidak berfungsi di zsh. Ada dua alasan gagal di zsh:
- sintaksnya
2>&3-
tidak dipahami oleh zsh; yang harus ditulis ulang sebagai2>&3 3>&-
- di zsh (tidak seperti shell lain), jika Anda mengarahkan deskriptor file yang sudah terbuka, dalam beberapa kasus (saya tidak sepenuhnya memahami cara memutuskannya), ia akan melakukan perilaku seperti tee bawaan. Untuk menghindari hal ini, Anda harus menutup setiap fd sebelum mengalihkannya.
Jadi, misalnya, solusi kedua saya harus ditulis ulang untuk zsh sebagai {my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(yang juga berfungsi di bash, tetapi sangat bertele-tele).
Di sisi lain, Anda dapat memanfaatkan tee implisit bawaan yang misterius dari zsh untuk mendapatkan solusi yang jauh lebih singkat untuk zsh, yang tidak menjalankan tee sama sekali:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(Saya tidak akan menebak dari dokumen yang saya temukan bahwa >&1
dan 2>&2
merupakan hal yang memicu tee implisit zsh; Saya mengetahuinya dengan trial-and-error.)