Saya ingin mengarahkan stdout dan stderr dari suatu proses ke satu file. Bagaimana saya melakukannya di Bash?
Saya ingin mengarahkan stdout dan stderr dari suatu proses ke satu file. Bagaimana saya melakukannya di Bash?
Jawaban:
Coba lihat di sini . Seharusnya:
yourcommand &>filename
(mengalihkan keduanya stdout
dan stderr
ke nama file).
#!/bin/bash
alih - alih #!/bin/sh
, karena di membutuhkan bash.
do_something 2>&1 | tee -a some_file
Ini akan mengarahkan stderr ke stdout dan stdout ke some_file
dan mencetaknya ke stdout.
do_something &>filename
tidak. +1.
Ambiguous output redirect.
mengapa?
$?
tidak lagi merujuk pada status keluar do_something
, tetapi status keluar tee
.
Anda dapat mengarahkan stderr ke stdout dan stdout ke file:
some_command >file.log 2>&1
Lihat http://tldp.org/LDP/abs/html/io-redirection.html
Format ini lebih disukai daripada format paling populer yang hanya berfungsi di bash. Dalam Bourne shell itu bisa diartikan sebagai menjalankan perintah di latar belakang. Formatnya juga lebih mudah dibaca 2 (adalah STDERR) dialihkan ke 1 (STDOUT).
EDIT: mengubah urutan seperti yang ditunjukkan dalam komentar
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Sekarang, gema sederhana akan menulis ke $ LOG_FILE. Berguna untuk daemonisasi.
Kepada penulis posting asli,
Itu tergantung apa yang perlu Anda capai. Jika Anda hanya perlu mengarahkan kembali / keluar dari perintah yang Anda panggil dari skrip Anda, jawabannya sudah diberikan. Milik saya adalah tentang mengarahkan ulang dalam skrip saat ini yang memengaruhi semua perintah / bawaan (termasuk garpu) setelah cuplikan kode yang disebutkan.
Solusi keren lainnya adalah tentang mengarahkan ulang ke std-err / out AND ke logger atau log file sekaligus yang melibatkan pemisahan "aliran" menjadi dua. Fungsionalitas ini disediakan oleh perintah 'tee' yang dapat menulis / menambahkan ke beberapa file deskriptor (file, soket, pipa, dll) sekaligus: tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Jadi, sejak awal. Mari kita asumsikan kita memiliki terminal yang terhubung ke / dev / stdout (FD # 1) dan / dev / stderr (FD # 2). Dalam praktiknya, bisa berupa pipa, soket atau apa pun.
Hasil menjalankan skrip yang memiliki baris di atas dan tambahan yang ini:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...adalah sebagai berikut:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Jika Anda ingin melihat gambar yang lebih jelas, tambahkan 2 baris ini ke skrip:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
menginstruksikan shell untuk mengirim STDOUT ke file file.log
, dan 2>&1
menyuruhnya untuk mengarahkan STDERR (deskriptor file 2) ke STDOUT (deskriptor file 1).
Catatan: Urutan penting seperti yang ditunjukkan oleh liw.fi, 2>&1 1>file.log
tidak berfungsi.
Anehnya, ini bekerja:
yourcommand &> filename
Tetapi ini memberikan kesalahan sintaksis:
yourcommand &>> filename
syntax error near unexpected token `>'
Anda harus menggunakan:
yourcommand 1>> filename 2>&1
&>>
tampaknya bekerja pada BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Jawaban singkat: Command >filename 2>&1
atauCommand &>filename
Penjelasan:
Pertimbangkan kode berikut yang mencetak kata "stdout" menjadi stdout dan kata "stderror" menjadi stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Perhatikan bahwa operator '&' memberi tahu bash bahwa 2 adalah deskriptor file (yang mengarah ke stderr) dan bukan nama file. Jika kita meninggalkan tanda '&', perintah ini akan mencetak stdout
ke stdout, dan membuat file bernama "2" dan menulis di stderror
sana.
Dengan bereksperimen dengan kode di atas, Anda dapat melihat sendiri bagaimana tepatnya pengalihan operator. Sebagai contoh, dengan mengubah file mana dari dua deskriptor 1,2
, diarahkan ke /dev/null
dua baris kode berikut, menghapus semuanya dari stdout, dan semuanya dari stderror masing-masing (mencetak apa yang tersisa).
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Sekarang, kita dapat menjelaskan mengapa solusi mengapa kode berikut tidak menghasilkan keluaran:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Untuk benar-benar memahami ini, saya sangat menyarankan Anda membaca halaman web ini pada tabel deskriptor file . Dengan asumsi Anda telah melakukan pembacaan itu, kami dapat melanjutkan. Perhatikan bahwa proses Bash dari kiri ke kanan; dengan demikian Bash melihat >/dev/null
terlebih dahulu (yang sama dengan 1>/dev/null
), dan menetapkan file deskriptor 1 untuk menunjuk ke / dev / null bukan stdout. Setelah melakukan ini, Bash kemudian bergerak ke kanan dan melihat 2>&1
. Ini mengatur file deskriptor 2 untuk menunjuk ke file yang sama dengan file deskriptor 1 (dan bukan ke file deskriptor 1 sendiri !!!!) (lihat sumber daya ini pada pointeruntuk info lebih lanjut)). Karena file deskriptor 1 menunjuk ke / dev / null, dan file deskriptor 2 menunjuk ke file yang sama dengan file deskriptor 1, file deskriptor 2 sekarang juga menunjuk ke / dev / null. Dengan demikian kedua deskriptor file menunjuk ke / dev / null, dan inilah sebabnya tidak ada output yang diberikan.
Untuk menguji apakah Anda benar-benar memahami konsep tersebut, coba tebak hasilnya ketika kami mengganti urutan pengalihan:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
Alasannya di sini adalah bahwa mengevaluasi dari kiri ke kanan, Bash melihat 2> & 1, dan dengan demikian mengatur file deskriptor 2 untuk menunjuk ke tempat yang sama dengan file deskriptor 1, yaitu stdout. Kemudian set file deskriptor 1 (ingat bahwa> / dev / null = 1> / dev / null) untuk menunjuk ke> / dev / null, sehingga menghapus semua yang biasanya akan dikirim ke standar keluar. Jadi yang tersisa adalah apa yang tidak dikirim ke stdout di subshell (kode dalam tanda kurung) - yaitu "stderror". Hal yang menarik untuk dicatat di sana adalah bahwa meskipun 1 hanya pointer ke stdout, mengarahkan pointer 2 ke 1 via 2>&1
TIDAK membentuk rantai pointer 2 -> 1 -> stdout. Jika ya, sebagai akibat dari mengarahkan 1 ke / dev / null, kode2>&1 >/dev/null
akan memberikan rantai penunjuk 2 -> 1 -> / dev / null, dan dengan demikian kode tidak akan menghasilkan apa-apa, berbeda dengan yang kita lihat di atas.
Akhirnya, saya perhatikan bahwa ada cara yang lebih sederhana untuk melakukan ini:
Dari bagian 3.6.4 di sini , kita melihat bahwa kita dapat menggunakan operator &>
untuk mengarahkan ulang stdout dan stderr. Dengan demikian, untuk mengarahkan kembali output stderr dan stdout dari perintah apa pun ke \dev\null
(yang menghapus output), kita cukup mengetik
$ command &> /dev/null
atau dalam kasus contoh saya:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
Takeaways kunci:
2>&1 >/dev/null
adalah! = >/dev/null 2>&1
. Satu menghasilkan output dan yang lainnya tidak!Akhirnya, lihat sumber daya hebat ini:
Dokumentasi Bash tentang Pengalihan , Penjelasan Tabel File Descriptor , Pengantar Pointer
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Ini terkait: Menulis stdOut & stderr ke syslog.
Hampir berhasil, tetapi bukan dari xinted; (
Saya ingin solusi agar keluaran dari stdout plus stderr ditulis ke dalam file log dan stderr masih di konsol. Jadi saya perlu menduplikasi output stderr melalui tee.
Ini adalah solusi yang saya temukan:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
Untuk situasi, ketika "perpipaan" diperlukan, Anda dapat menggunakan:
| &
Sebagai contoh:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
atau
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
Solusi berbasis bash ini dapat menyalurkan STDOUT dan STDERR secara terpisah (dari STDERR dari "sort -c" atau dari STDERR ke "sort -h").
Fungsi-fungsi berikut dapat digunakan untuk mengotomatiskan proses toggling output antara stdout / stderr dan file log.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Contoh penggunaan di dalam skrip:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
@ fernando-fabreti
Menambah apa yang Anda lakukan, saya sedikit mengubah fungsinya dan menghapus & - closing dan itu berfungsi untuk saya.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"
Dalam situasi ketika Anda mempertimbangkan untuk menggunakan hal-hal seperti exec 2>&1
saya merasa lebih mudah dibaca jika mungkin menulis ulang kode menggunakan fungsi bash seperti ini:
function myfunc(){
[...]
}
myfunc &>mylog.log