Salin stdout dan stderr ke file log dan biarkan di konsol di dalam skrip itu sendiri

11

Menggunakan bash, bagaimana cara menyalin stderr dan stdout ke file log dan juga membiarkannya ditampilkan di konsol?

Saya ingin melakukan ini di dalam skrip itu sendiri menggunakan exec.

Saya mencoba

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Tetapi di atas tidak mencetak apa pun di konsol. Bagaimana saya bisa mencapai ini di bash?

adarshr
sumber
Ada jawaban yang sangat tervotasikan untuk pertanyaan serupa di StackOverflow yang menjawab pertanyaan ini dengan cukup menyeluruh. stackoverflow.com/a/692407/208257
Dan Burton

Jawaban:

10

Anda sedang mencari tee.

Lihat man teedetailnya.

Untuk menggabungkannya exec, Anda harus menggunakan proses substitusi . (Lihat man bashdetailnya.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2
H.-Dirk Schmitt
sumber
Saya telah melihatnya. Melakukan exec 2>&1 | tee -a log.outhanya mencetak pada konsol, tidak ada dalam file log. Mungkin saya tidak menggunakan sintaks yang tepat?
adarshr
@adarshr Harap periksa ulang kode Anda. Saya telah menulis tentang teedalam kombinasi dengan proses substitusi .
H.-Dirk Schmitt
1
Itu menggabungkan stdout dan stderr dan dapat memiliki efek samping yang sama seperti yang disebutkan dalam jawaban saya.
Stéphane Chazelas
@StephaneChazelas Lihatlah contoh kode dalam pertanyaan - itulah maksud dari OP.
H.-Dirk Schmitt
Yang saya maksud dengan penggabungan adalah bahwa sekarang kesalahan skrip pergi ke stdout. Jadi, jika seseorang melakukannya the-script | wc -lmisalnya, itu juga akan menghitung garis kesalahan dan Anda tidak akan melihat kesalahan.
Stéphane Chazelas
5

Anda dapat melakukan:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Anda juga dapat menuliskannya sebagai:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Tetapi karena bash tidak menunggu proses-proses tersebut dimulai dengan >(...), yang memiliki efek buruk kadang-kadang mengeluarkan sesuatu ke terminal setelah perintah telah kembali yang dapat memiliki efek yang lebih buruk (seperti secara diam-diam membuang keluaran itu) jika terminal "untuk menghentikan" atribut aktif.

Dalam kasus apa pun, dengan membuat stdoutpipa di kedua solusi, dan karena dua perintah secara terpisah menampilkan output dan pesan kesalahan, ini akan mempengaruhi buffering output dan urutan output dan pesan kesalahan ditampilkan.

Stéphane Chazelas
sumber
Saya pikir tee kedua Anda tidak ada redirect ke stderr. Seharusnya:tee -a log >&2 3>&-
richvdh
5

Saya tahu ini adalah posting lama, tetapi mengapa tidak melakukan ini saja?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

Dan tentu saja, jika Anda ingin stdout Anda hanya dapat mencetak secara teratur.

Anda dapat melakukan ini dengan kombinasi aliran apa pun yang Anda inginkan, cukup menggunakan dua perintah dasar itu.

Saya tahu saya datang ke sini dan tidak mendapatkan jawaban yang mudah dimengerti / diterapkan, semoga ini akan membantu orang lain yang sedang berjuang.

By the way, untuk noobs di luar sana seperti diri saya sebelumnya, semua teeperintah yang dilakukan adalah output input stdin ke stdout dan file yang ditentukan sebagai argumen berikutnya. -asingkatan append, jadi Anda tidak menimpa file dengan setiap penggunaan perintah. Jika Anda memiliki pertanyaan lebih lanjut, saya menemukan ini sebagai sumber yang sangat membantu untuk mempelajari bash dengan cepat.

pengguna2624583
sumber
1
Terima kasih telah berkontribusi. Saya di sisi lain, benar-benar lupa apa yang saya coba lakukan :-)
adarshr
1
+1 untuk memberikan beberapa contoh tee yang bagus dan mudah.
Tim
2

Satu lagi cara melakukannya adalah menggunakan pengalihan dalam fungsi.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Di sini kita memiliki mainfungsi yang memanggil semua fungsi lainnya. Sekarang mengarahkan STDOUTdan STDERRdari mainfungsi untuk tee.

Kannan Mohan
sumber
IMHO ini adalah cara paling bersih untuk melakukan ini, setidaknya untuk kasus sederhana. Terima kasih.
ACK_stoverflow