Bagaimana cara mengarahkan stdout ke file, dan stdout + stderr ke yang lain?

32

Bagaimana saya bisa mencapainya?

cmd >> file1 2>&1 1>>file2

Artinya, stdout dan stderr harus diarahkan ke satu file (file1) dan hanya stdout (file2) yang harus diarahkan ke yang lain (keduanya dalam mode tambahan)?

Swarna Gowri
sumber

Jawaban:

41

Masalahnya adalah ketika Anda mengarahkan ulang output Anda, itu tidak tersedia lagi untuk redirect berikutnya. Anda dapat menyalurkan ke teedalam subkulit untuk menjaga output untuk pengalihan kedua:

( cmd | tee -a file2 ) >> file1 2>&1

atau jika Anda ingin melihat output di terminal:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

Untuk menghindari menambahkan stderr dari yang pertama teeke file1, Anda harus mengarahkan stderr dari perintah Anda ke beberapa deskriptor file (misalnya 3), dan kemudian menambahkan ini ke stdout lagi:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(terima kasih @ fra-san)

pLumo
sumber
16

Dengan zsh:

cmd >& out+err.log > out.log

Dalam mode tambahkan:

cmd >>& out+err.log >> out.log

Di zsh, dan asalkan mult_iosopsi belum dinonaktifkan, ketika file deskriptor (di sini 1) dialihkan beberapa kali untuk menulis, maka shell mengimplementasikan built-in teeuntuk menduplikasi output ke semua target.

Stéphane Chazelas
sumber
Saya tidak tahu apa out+errdan outmaksud di sini. Nama file? Streaming akan diarahkan?
gronostaj
@gronostaj Pikirkan bahwa perintah itu berbunyicmd >& file1 > file2
Isaac
Solusi ini akan mempertahankan urutan di mana output dihasilkan. Untuk benar-benar menyimpan stdout dan stderr (dalam urutan itu) Anda memerlukan pendekatan yang berbeda.
Isaac
1
@Isaac, pesanan tidak harus dipertahankan karena output stdout akan melalui pipa (ke proses yang meneruskannya ke setiap file) sementara output stderr akan langsung ke file. Bagaimanapun, itu tidak terlihat seperti OP meminta output stderr untuk datang setelah yang stdout.
Stéphane Chazelas
3

Anda dapat: menandai stdout (menggunakan sed UNBUFFERED, yaitu:) sed -u ..., memiliki stderr juga pergi ke stdout (tidak ditandai, karena tidak melewati sed penandaan itu), dan dengan demikian dapat membedakan 2 di logfile yang dihasilkan.

Berikut ini: lambat (Ini dapat dioptimalkan secara serius, dengan menggunakan untuk contoh skrip perl alih-alih sementara ...; lakukan ...; selesai, misalnya, yang akan memunculkan subkulit & perintah di setiap baris!), Aneh (sepertinya saya memerlukan 2 {} stage untuk di satu mengubah nama stdout dan kemudian yang lain menambahkan "jatuh melalui" stderr untuk itu), dll. Tapi itu adalah: " bukti konsep ", yang akan mencoba untuk menjaga urutan keluaran sebanyak-banyaknya dari stdout & stderr:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file
Olivier Dulac
sumber
Ini sangat sulit dimengerti. (1) Mengapa Anda menggunakan use case yang rumit ( ls unknown) untuk mencetak sesuatu di stderr? >&2 echo "error"akan baik-baik saja. (2) teedapat menambahkan beberapa file sekaligus. (3) Mengapa tidak hanya catbukan grep "^"? (4) skrip Anda akan gagal ketika output stderr dimulai _stdout_. (5) Kenapa?
pLumo
@RoVo: 2 baris komentar pertama menunjukkan algoritma, lebih sederhana daripada bukti contoh konsep. 1): ini ls loopakan menghasilkan output pada stdout dan stderr, dicampur (sebagai alternatif), dalam urutan yang terkendali, sehingga kita dapat memeriksa bahwa kita tetap memesan stderr / stdout meskipun ada penandaan stdout 2): gnu tail, mungkin, tapi tidak ekor biasa (mis., pada aix.). 3): grep "^" juga menampilkan kedua nama file. 4): ini dapat diubah oleh variabel. 5): contoh berbelit-belit bekerja pada oses lama (ex, aix tua) di mana saya mengujinya (tidak ada perl tersedia).
Olivier Dulac
(1) beberapa gema ke stderr dan stout akan baik-baik saja, tapi oke, tidak penting, hanya akan lebih mudah dibaca. (3) setuju, (4) yakin, tetapi akan gagal jika dimulai dengan apa pun yang berisi variabel. (5) Begitu.
pLumo
@RoVo Saya setuju dengan 1 Anda). untuk 4), variabel bisa serumit yang diperlukan untuk membuat pb menghilang (mis: uniquetag="banaNa11F453355B28E1158D4E516A2D3EDF96B3450406...)
Olivier Dulac
1
Tentu yakin, itu tidak terlalu mungkin, tetapi bagaimanapun itu mungkin akan memperkenalkan masalah keamanan di kemudian hari. Dan kemudian Anda mungkin ingin menghapus string itu sebelum mencetak ke file ;-)
pLumo
2

Jika urutan output harus: stdout maka stderr ; tidak ada solusi dengan pengalihan saja.
Stderr harus disimpan ke file temporal

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

Deskripsi:

Satu-satunya cara untuk mengarahkan ulang satu output (seperti stdout atau stderr) ke dua file adalah dengan mereproduksi. Perintah teeadalah alat yang benar untuk mereproduksi konten deskriptor file. Jadi, ide awal untuk memiliki satu output pada dua file adalah menggunakan:

... |  tee file1 file2

Itu mereproduksi stdin tee ke kedua file (1 & 2) meninggalkan output tee masih belum digunakan. Tetapi kita perlu menambahkan (menggunakan -a) dan hanya perlu satu salinan. Ini menyelesaikan kedua masalah:

... | tee -a file1 >>file2

Untuk memasok teedengan stdout (yang harus diulang) kita perlu mengkonsumsi stderr langsung dari perintah. Salah satu cara, jika pesanan tidak penting (urutan output akan (kemungkinan besar) akan dipertahankan seperti yang dihasilkan, yang mana output pertama akan disimpan terlebih dahulu). Antara:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

Opsi 2 hanya berfungsi di beberapa shell. Opsi 3 menggunakan subkulit tambahan (lebih lambat) tetapi gunakan nama file hanya sekali.

Tetapi jika stdout harus menjadi yang pertama (output pesanan mana pun yang dihasilkan) kita perlu menyimpan stderr untuk menambahkannya ke file di akhir (solusi pertama diposting).

Ishak
sumber
1
Atau simpan dalam memori seperti spongehalnya:(cmd | tee -a out >> out+err) 2>&1 | sponge >> out+err
Stéphane Chazelas
1

Untuk kepentingan keragaman:

Jika sistem Anda mendukung /dev/stderr, maka

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

akan bekerja. Output standar dari cmd dikirim ke stdout dan stderr dari pipa. Kesalahan standar cmdmemotong tee dan keluar stderr dari pipa.

Begitu

  • stdout dari pipa hanyalah stdout dari cmd, dan
  • stderr dari pipeline adalah stdout dan stderr dari cmd, intermixed.

Maka masalah sederhana mengirim aliran ke file yang benar.

Seperti hampir semua pendekatan seperti ini (termasuk jawaban Stéphane ), file1mungkin membuat garis tidak sesuai.

Scott
sumber