Paksa skrip bash untuk menggunakan tee tanpa piping dari baris perintah

20

Saya memiliki skrip bash dengan banyak output dan saya ingin mengubah skrip itu (bukan membuat yang baru) untuk menyalin semua output ke file, sama seperti itu akan terjadi jika saya menyalurkannya melalui tee.

Saya punya file script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

dan saya ingin membuat salinan STDOUT ke file script.log tanpa harus mengetik ./script.sh | tee script.logsetiap waktu.

Terima kasih, Tom

Tom
sumber
Saya minta maaf karena tidak mengatakan ini sebelumnya, tetapi skripnya agak panjang, itulah sebabnya saya mencari solusi sederhana. Menambahkan | tee untuk setiap baris agak terlalu banyak.
Tom

Jawaban:

15

Saya tidak bisa membuat Dennis satu kalimat yang sederhana, jadi inilah metode yang jauh lebih berbelit-belit. Saya akan mencoba yang pertama.

Seperti disebutkan, Anda dapat menggunakan exec untuk mengarahkan kesalahan standar & keluar standar untuk seluruh skrip. Seperti itu:
exec > $LOGFILE 2>&1 Ini akan menampilkan semua stderr dan stdout ke $ LOGFILE.

Sekarang, karena Anda ingin ini ditampilkan ke konsol dan juga file log, Anda juga harus menggunakan pipa bernama untuk exec untuk menulis, dan tee untuk membaca.
(Dennis satu-liner secara teknis melakukan ini juga, meskipun jelas dengan cara yang berbeda) Pipa itu sendiri dibuat dengan mkfifo $PIPEFILE. Kemudian lakukan hal berikut.

# Mulai menulis tee ke file log, tetapi menarik inputnya dari pipa bernama kami.
tee $ LOGFILE <$ PIPEFILE &

# tangkap ID proses tee untuk perintah tunggu.
TEEPID = $!

# redirect sisa stderr dan stdout ke pipa bernama kami.
exec> $ PIPEFILE 2> & 1

gema "Buat perintah Anda di sini"
gema "Semua standar mereka akan diupayakan."
gema "Begitu juga kesalahan standar mereka"> & 2

# tutup deskriptor file stderr dan stdout.
exec 1> & - 2> & -

# Tunggu tee selesai karena sekarang ujung pipa lainnya telah ditutup.
tunggu $ TEEPID

Jika Anda ingin teliti, Anda dapat membuat dan menghancurkan file pipa bernama di awal dan akhir skrip Anda.

Sebagai catatan, saya mendapatkan sebagian besar dari posting blog yang sangat informatif dari seorang pria: ( Versi yang diarsipkan )

Christopher Karel
sumber
Ini sepertinya tidak berhasil di cygwin. :-(
docwhat
Posting ini melakukan pekerjaan pendidikan yang baik. Terima kasih banyak.
Felipe Alvarez
27

Cukup tambahkan ini ke awal skrip Anda:

exec > >(tee -ia script.log)

Itu akan menambahkan semua output yang dikirim ke stdout ke file script.log, meninggalkan konten sebelumnya di tempat. Jika Anda ingin memulai baru setiap kali skrip dijalankan, tambahkan saja rm script.logtepat sebelum execperintah itu atau hapus -adari teeperintah.

The -ipilihan menyebabkan teeuntuk mengabaikan sinyal interrupt dan memungkinkan teeuntuk menangkap satu set output yang lebih lengkap.

Tambahkan baris ini untuk juga menangkap semua kesalahan (dalam file terpisah):

exec 2> >(tee -ia scripterr.out)

Ruang-ruang di antara banyak >simbol itu penting.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
Solusi yang sangat menarik.
ℝaphink
2
Ini adalah cara yang sangat elegan untuk melakukannya, dibandingkan dengan semua omong kosong yang saya posting. Tetapi ketika saya membuat skrip dengan dua baris di dalamnya, itu hang, dan tidak pernah keluar dengan benar.
Christopher Karel
Apa versi Bash?
Dijeda sampai pemberitahuan lebih lanjut.
GNU bash, versi 3.2.25 (1) sebagai bagian dari pemasangan RHEL5.3. Tampaknya ini adalah versi terbaru dari repositori resmi.
Christopher Karel
Saya baru saja mencobanya di bawah cygwin di Bash 3.2.49 (23) -release dan mendapat kesalahan deskriptor file. Saya bekerja di Bash 4.0.33 (1) - rilis di Ubuntu 9.10, jadi, mungkin itu adalah Bash 4.
Dijeda sampai pemberitahuan lebih lanjut.
6

Ini adalah versi gabungan dari jawaban yang diposting oleh Dennis Williamson sebelumnya. Tambahkan kedua & keluar ke script.log dalam urutan yang benar.

Tambahkan baris ini ke awal skrip Anda:

exec > >(tee -a script.log) 2>&1

Perhatikan jarak antar >tanda. Bekerja untuk saya di GNU bash, versi 3.2.25 (1) -release (x86_64-redhat-linux-gnu)

ttt
sumber
5

Saya kira pendekatan lain adalah sesuatu seperti ini:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
glenn jackman
sumber
3

Jika Anda ingin salinan output, Anda dapat menggunakan teecara ini:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Namun ini hanya akan mencatat stdout ke script.log. Jika Anda ingin memastikan stderr dan stdout diarahkan, gunakan:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Anda bahkan bisa membuatnya sedikit lebih bagus dengan fungsi kecil:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini
ℝaphink
sumber
Jawaban yang bagus. Jika dia benar-benar hanya peduli tentang stdout, saya hanya akan membiarkan 2>&1keluar dan hanya pipa ke tee.
DaveParillo
Itu benar, saya hanya memasukkan stderr karena jawaban sebelumnya dari Igor peduli untuk mengarahkan ulang dan saya pikir itu ide yang bagus.
ℝaphink
1

Anda baru saja menggunakan ini:

script logfile.txt
your script or command here

Ini menampilkan semua untuk log itu, semuanya, serta menampilkannya di layar. Jika Anda ingin output LENGKAP, ini adalah bagaimana melakukannya.

Dan Anda dapat memutar ulang skrip jika Anda malas.

Phishy
sumber
Mungkin baik untuk dicatat bahwa itu scriptadalah paket, mis. sudo apt-get install scriptuntuk menginstalnya pada distro rasa Debian , juga script -ac 'command opts' ~/Documents/some_log.scriptdapat ditinjau dalam terminal melalui sesuatu seperti strings ~/Documents/some_log.script | more; stringsmenjadi cara sederhana untuk melakukan pra-sanitasi beberapa hal yang script menyuntikkan untuk memungkinkan metode pemutaran ulang yang lebih menarik . Kalau tidak, jawaban yang solid @Fishy, ​​karena scriptjuga menjaga hal-hal interaktif juga.
S0AndS0
0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Anda dapat melihat lebih lanjut tentang cara kerjanya di sini: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/

Igor Zinov'yev
sumber
1
Saya menambahkan baris kedua ke baris kedua dalam jawaban Igor sehingga tidak menghapus log yang ditulis di baris pertama.
Doug Harris
1
Dia ingin salinannya, jadi dia perlu menggunakannya tee.
ℝaphink
@ Rafink: Itu benar :)
Tom