Saya tahu cara mengarahkan stdout ke file:
exec > foo.log
echo test
ini akan menempatkan 'test' ke dalam file foo.log.
Sekarang saya ingin mengarahkan output ke file log DAN tetap di stdout
yaitu dapat dilakukan secara sepele dari luar skrip:
script | tee foo.log
tapi saya ingin mendeklarasikannya dalam skrip itu sendiri
Saya mencoba
exec | tee foo.log
tapi itu tidak berhasil.
Jawaban:
Perhatikan bahwa ini
bash
bukansh
. Jika Anda memanggil skrip dengansh myscript.sh
, Anda akan mendapatkan kesalahan di sepanjang barissyntax error near unexpected token '>'
.Jika Anda bekerja dengan perangkap sinyal, Anda mungkin ingin menggunakan
tee -i
opsi untuk menghindari gangguan output jika sinyal terjadi. (Terima kasih kepada JamesThomasMoon1979 untuk komentarnya.)Alat-alat yang mengubah output mereka tergantung pada apakah mereka menulis ke pipa atau terminal (
ls
menggunakan warna dan output berbentuk kolom, misalnya) akan mendeteksi konstruk di atas sebagai makna bahwa mereka output ke pipa.Ada beberapa opsi untuk menerapkan pewarnaan / pengolomisasian (mis
ls -C --color=always
.). Perhatikan bahwa ini akan menghasilkan kode warna yang ditulis ke logfile juga, membuatnya kurang dapat dibaca.sumber
tee
seharusnya tidak buffer outputnya. Jika buffer pada sebagian besar sistem, itu rusak pada sebagian besar sistem. Itu masalahtee
implementasi, bukan solusi saya.exec
sangat kuat, tetapi juga sangat terlibat. Anda dapat "mencadangkan" stdout saat ini ke deskriptor yang berbeda, lalu memulihkannya nanti. Google "tutorial bash exec", ada banyak hal canggih di luar sana.exec
adalah didokumentasikan untuk tidak memulai proses baru,>(tee ...)
adalah standar pipa bernama / proses substitusi, dan&
dalam pengalihan tentu saja tidak ada hubungannya dengan pelatarbelakangan ... :-)?-i
untuktee
. Jika tidak, sinyal interupsi (jebakan) akan mengganggu stdout di skrip utama. Misalnya, jika Anda memilikitrap 'echo foo' EXIT
dan kemudian tekanctrl+c
, Anda tidak akan melihat " foo ". Jadi saya akan memodifikasi jawabannyaexec &> >(tee -ia file)
.Jawaban yang diterima tidak mempertahankan STDERR sebagai deskriptor file terpisah. Itu berarti
tidak akan keluaran
bar
ke terminal, hanya ke logfile, danakan menampilkan keduanya
foo
danbar
ke terminal. Jelas itu bukan perilaku yang biasa diharapkan oleh pengguna normal. Ini dapat diperbaiki dengan menggunakan dua proses tee terpisah yang keduanya ditambahkan ke file log yang sama:(Perhatikan bahwa di atas pada awalnya tidak memotong file log - jika Anda ingin perilaku itu Anda harus menambahkan
ke bagian atas skrip.)
The Spesifikasi POSIX.1-2008 dari
tee(1)
membutuhkan output yang unbuffered, yaitu tidak bahkan line-buffered, sehingga dalam hal ini adalah mungkin bahwa stdout dan stderr bisa berakhir pada baris yang sama darifoo.log
; Namun itu juga bisa terjadi pada terminal, sehingga file log akan menjadi cerminan yang setia dari apa yang bisa dilihat di terminal, jika bukan cermin yang tepat dari itu. Jika Anda ingin garis-garis STDOUT dipisahkan dengan bersih dari garis-garis STDERR, pertimbangkan untuk menggunakan dua file log, mungkin dengan awalan cap tanggal pada setiap baris untuk memungkinkan penyusunan kembali kronologis nanti.sumber
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
untuktee
. Jika tidak, sinyal interupsi (jebakan) akan mengganggu stdout dalam skrip. Misalnya, jika Andatrap 'echo foo' EXIT
lalu tekanctrl+c
, Anda tidak akan melihat " foo ". Jadi saya akan memodifikasi jawabannyaexec > >(tee -ia foo.log)
.. log
atau. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
muncul pertama kali sebagai batch, dan kemudian pesan akanSTDERR
muncul. Mereka tidak disisipkan seperti yang diharapkan.Solusi untuk busybox, macOS bash, dan cangkang non-bash
Jawaban yang diterima tentu saja merupakan pilihan terbaik untuk bash. Saya bekerja di lingkungan Busybox tanpa akses ke bash, dan itu tidak mengerti
exec > >(tee log.txt)
sintaks. Itu juga tidak melakukanexec >$PIPE
dengan benar, mencoba membuat file biasa dengan nama yang sama dengan pipa bernama, yang gagal dan hang.Semoga ini bermanfaat bagi orang lain yang tidak memiliki bash.
Juga, bagi siapa saja yang menggunakan pipa bernama, aman untuk itu
rm $PIPE
, karena itu memutuskan tautan pipa dari VFS, tetapi proses yang menggunakannya masih mempertahankan jumlah referensi di atasnya sampai selesai.Perhatikan penggunaan $ * belum tentu aman.
sumber
Di dalam file skrip Anda, masukkan semua perintah di dalam tanda kurung, seperti ini:
sumber
{}
)Cara mudah untuk membuat log skrip bash ke syslog. Keluaran skrip tersedia melalui
/var/log/syslog
dan melalui stderr. syslog akan menambahkan metadata yang berguna, termasuk cap waktu.Tambahkan baris ini di atas:
Atau, kirim log ke file terpisah:
Ini membutuhkan
moreutils
(untukts
perintah, yang menambahkan cap waktu).sumber
Menggunakan jawaban yang diterima skrip saya terus kembali sangat awal (tepat setelah 'exec>> (tee ...)') meninggalkan sisa skrip saya berjalan di latar belakang. Karena saya tidak bisa mendapatkan solusi untuk bekerja dengan cara saya, saya menemukan solusi lain / menyelesaikan masalah:
Ini membuat output dari skrip beralih dari proses, melalui pipa ke proses sub-latar belakang 'tee' yang mencatat semuanya ke disk dan ke stdout asli skrip.
Perhatikan bahwa 'exec &>' mengalihkan stdout dan stderr, kita dapat mengarahkan mereka secara terpisah jika kita mau, atau mengubah ke 'exec>' jika kita hanya ingin stdout.
Bahkan kamu pipa dihapus dari sistem file di awal skrip itu akan terus berfungsi sampai proses selesai. Kami tidak dapat merujuknya menggunakan nama file setelah rm-line.
sumber
$logfile
bagian daritee < ${logfile}.pipe $logfile &
. Secara khusus, saya mencoba mengubah ini untuk menangkap baris log perintah yang diperluas penuh (dariset -x
) ke file sementara hanya menampilkan baris tanpa memimpin '+' di stdout dengan mengubah(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
tetapi menerima pesan kesalahan mengenai$logfile
. Bisakah Anda menjelaskannyatee
sedikit lebih detail?Bash 4 memiliki
coproc
perintah yang menetapkan pipa bernama ke perintah dan memungkinkan Anda untuk berkomunikasi melalui itu.sumber
Tidak bisa mengatakan saya nyaman dengan salah satu solusi yang berbasis pada exec. Saya lebih suka menggunakan tee secara langsung, jadi saya membuat script memanggil dirinya sendiri dengan tee ketika diminta:
Ini memungkinkan Anda untuk melakukan ini:
Anda dapat menyesuaikan ini, misalnya membuat tee = false sebagai gantinya, membuat TEE menyimpan file log, dll. Saya kira solusi ini mirip dengan jbarlow, tetapi lebih sederhana, mungkin tambang saya memiliki batasan yang belum saya temui.
sumber
Tidak satu pun dari ini adalah solusi yang sempurna, tetapi di sini ada beberapa hal yang bisa Anda coba:
atau
Yang kedua akan meninggalkan file pipa duduk-duduk jika ada yang salah dengan skrip Anda, yang mungkin atau mungkin tidak menjadi masalah (yaitu mungkin Anda bisa
rm
melakukannya di shell induk setelahnya).sumber
tee
- Saya sudah mengedit. Seperti yang saya katakan, tidak ada solusi yang sempurna, tetapi proses latar belakang akan terbunuh ketika shell induknya berakhir, jadi Anda tidak perlu khawatir tentang mereka memonopoli sumber daya selamanya.