Arahkan ulang semua perintah berikutnya dengan menggunakan exec


43

Saya memiliki file bash yang saya perlukan untuk mengarahkan semua output ke satu file, debug log juga ke terminal. Saya perlu mengarahkan ulang stdout dan stderr ke debug dan mencatatnya untuk semua perintah dalam skrip.

Saya tidak ingin menambahkan 2>&1 | tee -a $DEBUGuntuk setiap perintah dalam file. Saya bisa hidup dengan | tee -a $DEBUG.

Saya ingat ada cara untuk melakukannya dengan sesuatu seperti exec 2>&1.

Saat ini saya menggunakan sesuatu seperti berikut:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

tetapi tidak berhasil. Adakah yang punya solusi / bisa menjelaskan penyebabnya?


1
Dalam beberapa shell, |&berfungsi sebagai jalan pintas 2>&1 |, setidaknya sedikit lebih nyaman.
Kevin

Jawaban:


39

Adapun solusi untuk mengarahkan banyak perintah sekaligus:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Mengapa solusi asli Anda tidak berfungsi: exec 2> & 1 akan mengarahkan output kesalahan standar ke output standar shell Anda, yang, jika Anda menjalankan skrip Anda dari konsol, akan menjadi konsol Anda. pengalihan pipa pada perintah hanya akan mengarahkan output standar dari perintah.

Pada sudut pandang somecommand, output standarnya masuk ke pipa yang terhubung teedan kesalahan standar masuk ke file / pseudofile yang sama dengan kesalahan standar shell, yang Anda arahkan ke output standar shell, yang akan menjadi konsol jika Anda menjalankan program Anda dari konsol.

Satu-satunya cara yang benar untuk menjelaskannya adalah dengan melihat apa yang sebenarnya terjadi:

Lingkungan asli shell Anda mungkin terlihat seperti ini jika Anda menjalankannya dari terminal:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Setelah Anda mengarahkan kesalahan standar ke output standar ( exec 2>&1), Anda ... pada dasarnya tidak mengubah apa pun. Tetapi jika Anda mengarahkan output skrip standar ke file, Anda akan berakhir dengan lingkungan seperti ini:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Kemudian mengarahkan kesalahan standar shell ke output standar akan berakhir seperti ini:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Menjalankan perintah akan mewarisi lingkungan ini. Jika Anda menjalankan perintah dan mengirimkannya ke tee, lingkungan perintahnya adalah:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Jadi kesalahan standar perintah Anda masih mencakup apa yang digunakan shell sebagai kesalahan standarnya.

Anda benar-benar dapat melihat lingkungan perintah dengan melihat /proc/[pid]/fd: gunakan ls -ljuga untuk membuat daftar konten tautan simbolik. The 0File sini adalah standar input, 1output standar dan 2standar error. Jika perintah membuka lebih banyak file (dan sebagian besar program melakukannya), Anda juga akan melihatnya. Suatu program juga dapat memilih untuk mengalihkan atau menutup input / output standar dan menggunakan kembali 0, 1dan 2.


41

Anda dapat menggunakan exec seperti ini di bagian atas skrip Anda:

exec > >(tee "$HOME/somefile.log") 2>&1

Sebagai contoh:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Memberi saya output ke file $HOME/somefile.logdan ke terminal seperti ini:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye

2
Perhatikan bahwa ini menggunakan bashisme - mungkin tidak bekerja di shell lain (misalnya, tanda hubung). Tetapi karena pertanyaan yang ditentukan bash, +1.
Richard Hansen

8
@RichardHansen, proses substitusi adalah fitur yang diperkenalkan oleh ksh, bukan bash dan juga didukung oleh zsh jadi saya tidak akan menyebutnya bashism .
Stéphane Chazelas

6
@StephaneChazelas: Anda membuat poin yang bagus. Saya hanya ingin menunjukkan bahwa sintaks tidak didukung oleh standar POSIX dan dengan demikian tidak akan bekerja secara universal dalam /bin/shskrip (banyak orang keliru menggunakan sintaks bash dalam /bin/shskrip).
Richard Hansen

Bagi saya ini memberikan /dev/fd/62: Operation not supportedpetunjuk?
Eun

1
Apakah ada cara untuk tidak mengarahkan stderr kecuali ke file log? Jika skrip asli myscriptdan saya jalankan ./myscript > /dev/null, saya masih harus melihat byeyang berasal echo bye >&2.
Martin Jambon

0

Tulis stderr dan stdout ke file, tampilkan stderr di layar (on stdout)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Berguna untuk crons, sehingga Anda dapat menerima kesalahan (dan hanya kesalahan) melalui surat

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.