Menulis keluaran ke file log dan konsol

98

Di shell Unix, saya memiliki file env (file env mendefinisikan parameter yang diperlukan untuk menjalankan skrip pengguna seperti nama dan jalur file log, mengarahkan output dan kesalahan ke file log, detail koneksi database, dll ) yang mengarahkan semua output ( pesan gema ) dan kesalahan pada file log dari skrip yang dieksekusi menggunakan kode berikut:

exec 1>>${LOG_FILE}
exec 2>>${LOG_FILE}

File env dijalankan di awal setiap skrip. Karena kode di atas dalam file env, semua output konsol yang mungkin merupakan output pengguna atau kesalahan secara langsung dikeluarkan ke file log yang sebenarnya saya butuhkan.

Tetapi ada beberapa keluaran pengguna selektif yang ingin saya tampilkan di konsol dan file log. Tetapi karena kode di atas saya tidak dapat melakukannya.

Saya tahu bahwa jika saya menghapus kode di atas, saya bisa mendapatkan hasil yang diinginkan untuk kasus ini, tetapi saya harus menulis semua output lainnya secara manual ke file log yang bukan merupakan tugas yang mudah.

Apakah ada cara untuk mendapatkan output di konsol dan file log tanpa menghapus kode di atas?

abinash shrestha
sumber

Jawaban:

105
exec 3>&1 1>>${LOG_FILE} 2>&1

akan mengirim output stdout dan stderr ke file log, tetapi juga akan membiarkan Anda dengan fd 3 terhubung ke konsol, jadi Anda bisa melakukannya

echo "Some console message" 1>&3

untuk menulis pesan hanya ke konsol, atau

echo "Some console and log file message" | tee /dev/fd/3

untuk menulis pesan ke kedua konsol dan file log - teemengirimkan output untuk kedua fd sendiri 1 (yang di sini adalah LOG_FILE) dan file Anda mengatakan itu untuk menulis ke (yang di sini adalah fd 3, yaitu konsol).

Contoh:

exec 3>&1 1>>${LOG_FILE} 2>&1

echo "This is stdout"
echo "This is stderr" 1>&2
echo "This is the console (fd 3)" 1>&3
echo "This is both the log and the console" | tee /dev/fd/3

akan mencetak

This is the console (fd 3)
This is both the log and the console

di konsol dan letakkan

This is stdout
This is stderr
This is both the log and the console

ke dalam file log.

Ian Roberts
sumber
2
Ini bekerja seperti yang Anda sarankan. Tapi saya tidak mengerti tee / dev / fd / 3 . Saya tahu bahwa tee menulis pesan ke file log dan konsol, tetapi saya tidak benar-benar memahami / dev / fd / 3 yang digunakan setelah tee
abinash shrestha
@shrestha itu penggunaan tee yang tidak biasa, saya setuju. Ini /dev/fd/3adalah nama file yang merujuk ke "fd 3 yang saat ini terbuka", jadi tee /dev/fd/3akan menulis apa pun yang masuk di stdin ke fd 1 dan juga fd 3 ( /dev/fd/3file). Fd 1 terhubung ke file log, fd 3 terhubung ke konsol.
Ian Roberts
Juga Anda hanya ingin menulis ke file dan bukan konsol coba: echo "blah" | tee file1.txt | tee file2.txt >/dev/null 'Bla' tidak akan diletakkan di file1.txt & file2.txt, tetapi tidak ditulis ke konsol.
hazard89
Ini sangat membantu saya. Meskipun saya melihat sesuatu yang mungkin di luar topik tetapi mungkin beberapa dari Anda tahu alasannya. Saya menjalankan skrip R dari skrip bash. Output konsol dari berbagai fungsi R berwarna (seperti yang saya definisikan). saat menggunakan pendekatan di atas untuk mengarahkan keluaran ke konsol DAN file log, keluaran konsol tidak lagi berwarna. Apa alasannya?
dieHellste
1
@dieHellste beberapa program dapat mendeteksi ketika output mereka sedang disalurkan ke proses lain (dalam hal ini tee, yang pada gilirannya menulis ke terminal) daripada langsung ke terminal, dan menyesuaikan outputnya agar sesuai.
Ian Roberts
43

Ya, Anda ingin menggunakan tee:

tee - membaca dari input standar dan menulis ke output dan file standar

Cukup pipa perintah Anda ke tee dan teruskan file sebagai argumen, seperti:

exec 1 | tee ${LOG_FILE}
exec 2 | tee ${LOG_FILE}

Ini mencetak output ke STDOUT dan menulis output yang sama ke file log. Lihat man teeuntuk informasi lebih lanjut.

Perhatikan bahwa ini tidak akan menulis stderr ke file log, jadi jika Anda ingin menggabungkan dua aliran, gunakan:

exec 1 2>&1 | tee ${LOG_FILE}
Jon Cairns
sumber
2
Solusi di atas tidak berhasil. Saya memiliki file redirect.env sebagai: ##### redirect.env ###### ekspor LOG_FILE = log.txt exec 1 2> & 1 | tee -a $ {LOG_FILE} exec 1 | tee -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} ######### dan file kerja berisi kode berikut: ##### output.sh ##### #! / bin / sh. redirect.env echo "Output valid" ech "output tidak valid" ############## tetapi saya mendapatkan kesalahan: #### redirect.env: baris 3: exec: 1: tidak ditemukan redirect.env: baris 5: exec: 1: tidak ditemukan redirect.env: baris 6: exec: 2: tidak ditemukan #### dan di file log saya mendapatkan kesalahan yang sama juga. Apakah saya melakukan sesuatu yang salah?
abinash shrestha
Sangat sulit untuk mengatakannya, karena baris baru dihilangkan dalam komentar Anda. Bisakah Anda menambahkan tempel kode di suatu tempat seperti inti ?
Jon Cairns
1
Saya telah menambahkan file ke tautan UnixRedirect . File terkait adalah redirect.env dan output.sh
abinash shrestha
1
Kode ini sepertinya tidak berfungsi (setidaknya tidak lagi). Anda biasanya akan mendapatkanscript.sh: line 5: exec: 1: not found
tftd
39

Saya mencoba jawaban joonty, tetapi saya juga mendapatkan

exec: 1: tidak ditemukan

kesalahan. Inilah yang paling cocok untuk saya ( dipastikan juga berfungsi di zsh):

#!/bin/bash
LOG_FILE=/tmp/both.log
exec > >(tee ${LOG_FILE}) 2>&1
echo "this is stdout"
chmmm 77 /makeError

File /tmp/both.log sesudahnya berisi

this is stdout
chmmm command not found 

/Tmp/both.log ditambahkan kecuali Anda menghapus -a dari tee.

Petunjuk: >(...)adalah substitusi proses. Ini memungkinkan execke teeperintah seolah-olah itu adalah file.

alfonx.dll
sumber
1
Ini bekerja seperti pesona bagi saya, sedangkan jawaban lainnya terpukul atau terlewatkan.
Jay Taylor
Terima kasih telah membagikan cuplikan ini. Ini tampaknya berfungsi dengan baik (dibandingkan dengan jawaban lain)!
tftd
Ini berfungsi, tetapi sekarang shell saya berbeda setelah eksekusi.
Josh Usre
@JoshUsre Jika Anda ingin bantuan atau Anda ingin membantu pengguna SO lainnya, jelaskan apa yang berbeda, dan jelaskan shell, versi, OS dll ..
alfonx
1
Jika Anda tidak perlu membedakan antara stdout dan stderr, Anda dapat menggabungkan pernyataan ke exec > >(tee ${LOG_FILE}) 2>&1.
darkdragon
5

Saya ingin menampilkan log di stdout dan file log bersama dengan stempel waktu. Tak satu pun dari jawaban di atas berhasil untuk saya. Saya menggunakan substitusi proses dan perintah exec dan menghasilkan kode berikut. Contoh log:

2017-06-21 11:16:41+05:30 Fetching information about files in the directory...

Tambahkan baris berikut di bagian atas skrip Anda:

LOG_FILE=script.log
exec > >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done)
exec 2> >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done >&2)

Semoga ini bisa membantu seseorang!

Jyoti Dhiman
sumber
Apakah mungkin untuk mencatat semua kesalahan aplikasi untuk masuk ke direktori host sehingga ini akan membantu pengembang ....
Prasad Shinde
3

untuk file log Anda mungkin tanggal untuk memasukkan ke dalam data teks. kode berikut dapat membantu

# declaring variables

Logfile="logfile.txt"   
MAIL_LOG="Message to print in log file"  
Location="were is u want to store log file"

cd $Location   
if [ -f $Logfile ]  
then   
echo "$MAIL_LOG " >> $Logfile

else        

touch $Logfile   
echo "$MAIL_LOG" >> $Logfile    

fi  

ouput: 2. File log akan dibuat pada proses pertama dan terus diperbarui dari proses berikutnya. Jika file log hilang di masa mendatang, skrip akan membuat file log baru.

pengguna2197712
sumber
1

Saya telah menemukan cara untuk mendapatkan hasil yang diinginkan. Padahal cara itu mungkin agak tidak ortodoks. Anyways ini dia. Di file redir.env saya memiliki kode berikut:

#####redir.env#####    
export LOG_FILE=log.txt

      exec 2>>${LOG_FILE}

    function log {
     echo "$1">>${LOG_FILE}
    }

    function message {
     echo "$1"
     echo "$1">>${LOG_FILE}
    }

Kemudian di skrip sebenarnya saya memiliki kode berikut:

#!/bin/sh 
. redir.env
echo "Echoed to console only"
log "Written to log file only"
message "To console and log"
echo "This is stderr. Written to log file only" 1>&2

Berikut menggemakan output hanya untuk konsol, log output hanya file log dan pesan output untuk kedua file log dan konsol.

Setelah menjalankan file skrip di atas, saya memiliki output berikut:

Di konsol

Di konsol
Gema ke konsol hanya
Untuk konsol dan log

Untuk file Log

Dalam File Log Ditulis ke file log saja
Ini stderr. Ditulis ke file log hanya
Untuk konsol dan log

Semoga bantuan ini.

abinash shrestha
sumber
1

Coba ini, ini akan berhasil:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
amousa
sumber
Ini exec > >(tee -a ${log_file} )bekerja sempurna untuk kebutuhan saya. Solusi sebelumnya di atas akan mengganggu bagian skrip saya yang memaksa keluar jika gagal. Terima kasih
MitchellK
1
    #
    #------------------------------------------------------------------------------
    # echo pass params and print them to a log file and terminal
    # with timestamp and $host_name and $0 PID
    # usage:
    # doLog "INFO some info message"
    # doLog "DEBUG some debug message"
    # doLog "WARN some warning message"
    # doLog "ERROR some really ERROR message"
    # doLog "FATAL some really fatal message"
    #------------------------------------------------------------------------------
    doLog(){
        type_of_msg=$(echo $*|cut -d" " -f1)
        msg=$(echo "$*"|cut -d" " -f2-)
        [[ $type_of_msg == DEBUG ]] && [[ $do_print_debug_msgs -ne 1 ]] && return
        [[ $type_of_msg == INFO ]] && type_of_msg="INFO " # one space for aligning
        [[ $type_of_msg == WARN ]] && type_of_msg="WARN " # as well

        # print to the terminal if we have one
        test -t 1 && echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg"

        # define default log file none specified in cnf file
        test -z $log_file && \
            mkdir -p $product_instance_dir/dat/log/bash && \
                log_file="$product_instance_dir/dat/log/bash/$run_unit.`date "+%Y%m"`.log"
        echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg" >> $log_file
    }
    #eof func doLog
Yordan Georgiev
sumber
0

Saya merasa sangat berguna untuk menambahkan stdout dan stderr ke file log. Saya senang melihat solusi dengan alfonx dengan exec > >(tee -a), karena saya bertanya-tanya bagaimana cara melakukannya dengan menggunakan exec. Saya menemukan solusi kreatif menggunakan sintaks here-doc dan .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -naskah

Saya menemukan bahwa di zsh, solusi here-doc dapat dimodifikasi menggunakan konstruksi "multios" untuk menyalin keluaran ke stdout / stderr dan file log:

#!/bin/zsh
LOG=$0.log
# 8 is an arbitrary number;
# multiple redirects for the same file descriptor 
# triggers "multios"
. 8<<\EOF /dev/fd/8 2>&2 >&1 2>>$LOG >>$LOG
# some commands
date >&2
set -x
echo hi
echo bye
EOF
echo not logged

Ini tidak dapat dibaca sebagai execsolusinya tetapi memiliki keuntungan memungkinkan Anda untuk mencatat hanya sebagian dari skrip. Tentu saja, jika Anda menghilangkan EOF maka seluruh skrip akan dieksekusi dengan logging. Saya tidak yakin bagaimana zshmengimplementasikan multios, tetapi mungkin memiliki lebih sedikit overhead daripada tee. Sayangnya tampaknya multios tidak dapat digunakan dengan exec.

Metamorf
sumber