Saya memiliki skrip yang akan dijalankan secara interaktif oleh pengguna non-teknis. Skrip menulis pembaruan status ke STDOUT sehingga pengguna dapat yakin bahwa skrip berjalan dengan baik.
Saya ingin STDOUT dan STDERR dialihkan ke terminal (sehingga pengguna dapat melihat bahwa skrip berfungsi serta melihat apakah ada masalah). Saya juga ingin kedua aliran dialihkan ke file log.
Saya telah melihat banyak solusi di internet. Beberapa tidak berfungsi dan yang lainnya sangat rumit. Saya telah mengembangkan solusi yang bisa diterapkan (yang akan saya masukkan sebagai jawaban), tetapi itu kludgy.
Solusi sempurna adalah satu baris kode yang dapat digabungkan ke awal skrip apa pun yang mengirimkan kedua aliran ke terminal dan file log.
EDIT: Mengalihkan STDERR ke STDOUT dan menyalurkan hasilnya ke tee berfungsi, tetapi itu tergantung pada pengguna yang mengingat untuk mengalihkan dan menyalurkan output. Saya ingin logging menjadi sangat mudah dan otomatis (itulah sebabnya saya ingin dapat menyematkan solusi ke dalam skrip itu sendiri.)
Jawaban:
Gunakan "tee" untuk mengarahkan ke file dan layar. Bergantung pada shell yang Anda gunakan, pertama-tama Anda harus mengarahkan stderr ke stdout menggunakan
atau
Di csh, ada perintah built-in yang disebut "script" yang akan menangkap semua yang masuk ke layar ke sebuah file. Anda memulainya dengan mengetik "script", lalu melakukan apa pun yang ingin Anda tangkap, lalu tekan control-D untuk menutup file skrip. Saya tidak tahu padanan untuk sh / bash / ksh.
Juga, karena Anda telah menunjukkan bahwa ini adalah skrip sh Anda sendiri yang dapat Anda modifikasi, Anda dapat melakukan pengalihan secara internal dengan mengelilingi seluruh skrip dengan tanda kurung atau tanda kurung, seperti
sumber
2>&1 | tee -a filename
tidak menyimpan stderr ke file dari skrip saya, tetapi berfungsi dengan baik ketika saya menyalin perintah dan menempelkannya ke terminal! Trik braket berfungsi dengan baik.Mendekati setengah dekade kemudian ...
Saya yakin ini adalah "solusi sempurna" yang dicari oleh OP.
Inilah satu liner yang dapat Anda tambahkan ke bagian atas skrip Bash Anda:
Berikut ini skrip kecil yang mendemonstrasikan penggunaannya:
(Catatan: Ini hanya berfungsi dengan Bash. Ini tidak akan berfungsi dengan / bin / sh.)
Diadaptasi dari sini ; yang asli tidak, dari apa yang saya tahu, menangkap STDERR di logfile. Diperbaiki dengan catatan dari sini .
sumber
Pola
Ini mengalihkan baik stdout dan stderr secara terpisah, dan mengirimkan salinan terpisah dari stdout dan stderr ke pemanggil (yang mungkin merupakan terminal Anda).
Dalam zsh, ini tidak akan dilanjutkan ke pernyataan berikutnya sampai
tee
s selesai.Di bash, Anda mungkin menemukan bahwa beberapa baris keluaran terakhir muncul setelah pernyataan apa pun yang muncul berikutnya.
Dalam kedua kasus tersebut, bit yang tepat menuju ke tempat yang tepat.
Penjelasan
Berikut skripnya (disimpan dalam ./example):
Inilah sesi:
Begini cara kerjanya:
tee
proses dimulai, stdinsnya ditetapkan ke deskriptor file. Karena mereka tertutup dalam substitusi proses , jalur ke deskriptor file tersebut diganti dalam perintah pemanggil, jadi sekarang terlihat seperti ini:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
berjalan, menulis stdout ke deskriptor file pertama, dan stderr ke deskriptor kedua.Dalam kasus bash, setelah
the_cmd
selesai, pernyataan berikut segera terjadi (jika terminal Anda adalah pemanggil, maka Anda akan melihat prompt Anda muncul).Dalam kasus zsh, setelah
the_cmd
selesai, shell menunggu keduatee
proses selesai sebelum melanjutkan. Lebih lanjut tentang ini di sini .Proses pertama
tee
, yang membaca darithe_cmd
stdout, menulis salinan dari stdout itu kembali ke pemanggil karena itulah yangtee
dilakukannya. Keluarannya tidak dialihkan, sehingga membuatnya kembali ke pemanggil tanpa diubahProses kedua
tee
telahstdout
dialihkan ke pemanggilstderr
(yang bagus, karena stdin sedang membaca darithe_cmd
stderr). Jadi ketika menulis ke stdout-nya, bit-bit itu pergi ke stderr pemanggil.Ini membuat stderr terpisah dari stdout baik dalam file maupun dalam output perintah.
Jika tee pertama menulis kesalahan apa pun, kesalahan tersebut akan muncul di file stderr dan di stderr perintah, jika tee kedua menulis kesalahan, kesalahan tersebut hanya akan muncul di stderr terminal.
sumber
tee
tersedia pada sistem yang dimaksud.) Kesalahan yang saya dapatkan adalah "Proses tidak dapat mengakses file karena sedang digunakan oleh proses lain."untuk mengarahkan stderr ke stdout tambahkan ini di perintah Anda:
2>&1
Untuk keluaran ke terminal dan masuk ke file, Anda harus menggunakantee
Keduanya akan terlihat seperti ini:
EDIT: Untuk menyematkan ke skrip Anda, Anda akan melakukan hal yang sama. Jadi naskahmu
akan berakhir sebagai
sumber
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:
Blok penyusun kedua: jika kita ingin memfilter (misalnya tee) hanya stderr, kita dapat melakukannya dengan menukar stdout & stderr, memfilter, dan kemudian menukar kembali:
Sekarang sisanya mudah: kita bisa menambahkan filter stdout, baik di awal:
atau di akhir:
Untuk meyakinkan diri sendiri bahwa kedua perintah di atas berfungsi, saya menggunakan yang berikut ini:
Outputnya adalah:
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:
2>&3-
tidak dipahami oleh zsh; yang harus ditulis ulang sebagai2>&3 3>&-
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:
(Saya tidak akan menebak dari dokumen yang saya temukan bahwa
>&1
dan2>&2
merupakan hal yang memicu tee implisit zsh; Saya mengetahuinya dengan trial-and-error.)sumber
my_function
) di pemfilteran stdout / stderr bersarang? Saya melakukannya{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
tetapi rasanya aneh membuat file sebagai indikator kegagalan ...Menggunakan
script
perintah dalam skrip Anda (skrip man 1)Buat shellscript pembungkus (2 baris) yang menyiapkan script () dan kemudian memanggil exit.
Bagian 1: wrap.sh
Bagian 2: realscript.sh
Hasil:
sumber
Gunakan program tee dan dup stderr untuk stdout.
sumber
Saya membuat skrip yang disebut "RunScript.sh". Isi dari script ini adalah:
Saya menyebutnya seperti ini:
Ini berfungsi, tetapi membutuhkan skrip aplikasi untuk dijalankan melalui skrip eksternal. Agak kludgy.
sumber
Setahun kemudian, inilah skrip bash lama untuk mencatat apa pun. Misalnya,
teelog make ...
membuat log ke nama log yang dihasilkan (dan lihat trik untuk membuat log bersarangmake
juga.)sumber