bagaimana cara menampilkan teks ke layar dan file di dalam skrip shell?

49

Saat ini saya memiliki skrip shell yang mencatat pesan ke file log seperti ini:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

Ketika menjalankan skrip ini, tidak ada output ke layar, dan, karena saya terhubung ke server melalui dempul, saya harus membuka koneksi lain dan melakukan "tail -f log_file_path.log", karena saya tidak dapat menghentikan jalannya skrip dan saya ingin melihat output secara real time.

Jelas, yang saya inginkan adalah bahwa pesan teks dicetak pada layar dan ke dalam file, tetapi saya ingin melakukannya dalam satu baris, bukan dua baris, yang salah satunya tidak memiliki pengalihan ke file.

Bagaimana cara mencapai ini?

Jurchiks
sumber

Jawaban:

71

Ini bekerja:

command | tee -a "$log_file"

teemenyimpan input ke file (gunakan -auntuk menambahkan daripada menimpa), dan menyalin input ke output standar juga.

l0b0
sumber
8

Anda dapat menggunakan sini-doc dan. sumbernya untuk model kolektor umum yang efisien, ramah-POSIX.

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

Ketika Anda membuka heredoc, Anda memberi isyarat shell dengan token input IOHERE bahwa ia harus mengarahkan inputnya ke file-deskriptor yang Anda tentukan sampai bertemu ujung lain token pembatas Anda. Saya telah melihat-lihat tetapi saya belum melihat banyak contoh penggunaan nomor pengalihan fd seperti yang saya tunjukkan di atas dalam kombinasi dengan operator heredoc, meskipun penggunaannya jelas ditentukan dalam pedoman dasar perintah shell-perintah POSIX. Kebanyakan orang hanya mengarahkannya ke stdin dan menembak, tetapi saya menemukan sumber scriptlets dengan cara ini dapat menjaga stdin gratis dan aplikasi konstituen mengeluh tentang jalur I / o yang diblokir.

Konten heredoc dialirkan ke deskriptor file yang Anda tentukan, yang kemudian ditafsirkan sebagai shell-code dan dieksekusi oleh. builtin, tetapi bukan tanpa menentukan jalur khusus untuk. . Jika / proc / self path memberi Anda masalah, coba / dev / fd / n atau / proc / $$. Metode yang sama ini bekerja pada pipa, dengan cara:

cat ./*.sh | . /dev/stdin

Mungkin setidaknya tidak bijaksana seperti kelihatannya. Anda dapat melakukan hal yang sama dengan sh, tentu saja, tetapi. Tujuannya adalah untuk mengeksekusi di lingkungan shell saat ini, yang mungkin apa yang Anda inginkan, dan, tergantung pada shell Anda, jauh lebih mungkin untuk bekerja dengan heredoc daripada dengan pipa anonim standar.

Bagaimanapun, seperti yang mungkin Anda perhatikan, saya masih belum menjawab pertanyaan Anda. Tetapi jika Anda berpikir tentang hal itu, dengan cara yang sama heredoc mengalirkan semua kode Anda. Masuk, itu juga memberi Anda satu, sederhana, outpoint:

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

Jadi semua terminal stdout dari salah satu kode yang dieksekusi di heredoc Anda keluar dari pipa. sebagai hal yang biasa dan dapat dengan mudah diambil dari satu pipa. Saya memasukkan panggilan kucing yang tidak terganggu karena saya tidak jelas tentang arah stdout saat ini, tetapi mungkin redundan (hampir pasti seperti yang tertulis) dan saluran pipa mungkin dapat berakhir tepat di tee.

Anda mungkin juga mempertanyakan kutipan backslash yang hilang dalam contoh kedua. Bagian ini penting untuk dipahami sebelum Anda terjun dan mungkin memberi Anda beberapa ide tentang cara penggunaannya. Sebuah heredoc limiter yang dikutip (sejauh ini kami telah menggunakan IOHERE dan EOIN, dan yang pertama saya kutip dengan backslash, meskipun kutipan 'tunggal' atau "ganda" akan memiliki tujuan yang sama) akan menghalangi shell untuk melakukan ekspansi parameter pada konten, tetapi pembatas tanpa tanda kutip akan membiarkan isinya terbuka untuk ekspansi. Konsekuensi dari ini ketika heredoc Anda. bersumber dramatis:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

Karena saya tidak mengutip heredoc limiter, shell memperluas isinya ketika membacanya di dan sebelum menyajikan deskriptor file yang dihasilkan. untuk mengeksekusi. Ini pada dasarnya menghasilkan perintah yang diurai dua kali - yang diperluas. Karena saya backslash mengutip ekspansi parameter $ vars, shell mengabaikan deklarasi pada pass pertama dan hanya menghapus backslash sehingga seluruh isi printf yang diperluas dapat dievaluasi dengan nol saat. bersumber skrip pada pass kedua.

Fungsionalitas ini pada dasarnya adalah persis apa yang dapat disediakan oleh shell eval berbahaya, bahkan jika penawarannya lebih mudah ditangani dalam heredoc daripada dengan eval, dan bisa sama berbahayanya. Kecuali Anda merencanakannya dengan hati-hati, mungkin yang terbaik adalah mengutip pembatas "EOF" sebagai kebiasaan. Hanya mengatakan.

EDIT: Eh, saya melihat kembali ini dan berpikir itu terlalu berlebihan. Jika SEMUA yang perlu Anda lakukan adalah menggabungkan beberapa output menjadi satu pipa maka metode paling sederhana adalah dengan menggunakan:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

Curlies akan berusaha menjalankan konten di shell saat ini sedangkan parens akan sub-out secara otomatis. Namun, siapa pun dapat memberi tahu Anda itu dan, setidaknya menurut saya,. solusi heredoc adalah informasi yang jauh lebih berharga, terutama jika Anda ingin memahami cara kerja shell.

Selamat bersenang-senang!

mikeserv
sumber
3

Saya menemukan jawaban ini sambil mencari untuk memodifikasi skrip instalasi untuk menulis log instalasi.

Skrip saya sudah penuh dengan pernyataan gema seperti:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

Dan saya tidak ingin pernyataan tee untuk menjalankannya (atau skrip lain untuk memanggil yang sudah ada dengan tee), jadi saya menulis ini:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Jadi sekarang dalam skrip asli saya, saya bisa mengubah 'echo' menjadi 'echolog' di mana saya ingin output dalam file log.

AndruWitta
sumber