Saya memiliki dua proses foo
dan bar
, terhubung dengan pipa:
$ foo | bar
bar
selalu keluar 0; Saya tertarik pada kode keluar dari foo
. Apakah ada cara untuk melakukannya?
Saya memiliki dua proses foo
dan bar
, terhubung dengan pipa:
$ foo | bar
bar
selalu keluar 0; Saya tertarik pada kode keluar dari foo
. Apakah ada cara untuk melakukannya?
Jawaban:
Jika Anda menggunakan bash
, Anda bisa menggunakan PIPESTATUS
variabel array untuk mendapatkan status keluar dari setiap elemen pipa.
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
Jika Anda menggunakan zsh
, array mereka disebut pipestatus
(hal penting!) Dan indeks array dimulai pada satu:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
Untuk menggabungkan mereka dalam suatu fungsi dengan cara yang tidak kehilangan nilai:
$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0
Jalankan hal di atas dalam bash
atau zsh
dan Anda akan mendapatkan hasil yang sama; hanya satu dari retval_bash
dan retval_zsh
akan ditetapkan. Yang lain akan kosong. Ini akan memungkinkan fungsi berakhir dengan return $retval_bash $retval_zsh
(perhatikan kurangnya tanda kutip!).
pipestatus
di zsh. Sayangnya cangkang lain tidak memiliki fitur ini.
echo "$pipestatus[1]" "$pipestatus[2]"
.
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
Ada 3 cara umum untuk melakukan ini:
Cara pertama adalah mengatur pipefail
opsi ( ksh
, zsh
atau bash
). Ini adalah yang paling sederhana dan apa yang dilakukannya pada dasarnya adalah mengatur status keluar $?
ke kode keluar dari program terakhir untuk keluar non-nol (atau nol jika semua keluar dengan sukses).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash juga memiliki variabel array bernama $PIPESTATUS
( $pipestatus
in zsh
) yang berisi status keluar dari semua program dalam pipa terakhir.
$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1
Anda bisa menggunakan contoh perintah ke-3 untuk mendapatkan nilai spesifik dalam pipa yang Anda butuhkan.
Ini adalah solusi yang paling sulit. Jalankan setiap perintah secara terpisah dan tangkap statusnya
$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
ksh
, tapi dari pandangan singkat di halaman manualnya, itu tidak mendukung $PIPESTATUS
atau yang serupa. Itu mendukung pipefail
opsi itu.
LOG=$(failed_command | successful_command)
Solusi ini berfungsi tanpa menggunakan fitur spesifik bash atau file sementara. Bonus: pada akhirnya status keluar sebenarnya adalah status keluar dan bukan string dalam file.
Situasi:
someprog | filter
Anda menginginkan status keluar dari someprog
dan keluaran dari filter
.
Ini solusinya:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
hasil dari konstruk ini adalah stdout dari filter
sebagai stdout dari konstruk dan status keluar dari someprog
sebagai status keluar dari konstruk.
konstruksi ini juga berfungsi dengan pengelompokan perintah sederhana {...}
alih-alih subkulit (...)
. subkulit memiliki beberapa implikasi, antara lain biaya kinerja, yang tidak kita butuhkan di sini. baca manual bash halus untuk detail lebih lanjut: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
Sayangnya tata bahasa bash membutuhkan ruang dan titik koma untuk kurung kurawal sehingga konstruksinya menjadi jauh lebih luas.
Untuk sisa teks ini saya akan menggunakan varian subkulit.
Contoh someprog
dan filter
:
someprog() {
echo "line1"
echo "line2"
echo "line3"
return 42
}
filter() {
while read line; do
echo "filtered $line"
done
}
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
Contoh output:
filtered line1
filtered line2
filtered line3
42
Catatan: proses anak mewarisi deskriptor file terbuka dari induknya. Itu berarti someprog
akan mewarisi deskriptor file terbuka 3 dan 4. Jika someprog
menulis ke file deskriptor 3 maka itu akan menjadi status keluar. Status keluar sebenarnya akan diabaikan karena read
hanya dibaca sekali.
Jika Anda khawatir bahwa Anda someprog
mungkin menulis ke deskriptor 3 atau 4, maka yang terbaik adalah menutup deskriptor file sebelum memanggil someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
The exec 3>&- 4>&-
sebelum someprog
menutup file descriptor sebelum mengeksekusi someprog
jadi untuk someprog
file yang mereka deskriptor sama sekali tidak ada.
Bisa juga ditulis seperti ini: someprog 3>&- 4>&-
Langkah demi langkah penjelasan konstruk:
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
Dari bawah ke atas:
#part3
) dan kanan ( #part2
) dieksekusi. exit $xs
juga merupakan perintah terakhir dari pipa dan itu berarti string dari stdin akan menjadi status keluar dari keseluruhan konstruk.#part2
dan pada gilirannya akan menjadi status keluar dari keseluruhan konstruk.#part5
dan #part6
) dan kanan ( filter >&4
) dieksekusi. Output dari filter
diarahkan ke file deskriptor 4. Pada #part1
file deskriptor 4 diarahkan ke stdout. Ini berarti bahwa output filter
adalah stdout dari keseluruhan konstruk.#part6
dicetak ke file deskriptor 3. Pada #part3
file deskriptor 3 dialihkan ke #part2
. Ini berarti bahwa status keluar dari #part6
akan menjadi status keluar akhir untuk keseluruhan konstruk.someprog
dieksekusi. Status keluar diambil #part5
. Stdout diambil oleh pipa masuk #part4
dan diteruskan ke filter
. Output dari filter
pada gilirannya akan mencapai stdout seperti yang dijelaskan dalam#part4
(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)
disederhanakan menjadi someprog 3>&- 4>&-
.
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
Meskipun tidak persis apa yang Anda minta, Anda bisa menggunakannya
#!/bin/bash -o pipefail
sehingga pipa Anda mengembalikan bukan nol kembali terakhir.
mungkin sedikit kurang coding
Edit: Contoh
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefail
di dalam skrip harus lebih kuat, misalnya jika seseorang mengeksekusi skrip via bash foo.sh
.
-o pipefail
tidak ada dalam POSIX.
#!/bin/bash -o pipefail
. Kesalahan adalah:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!
garis di luar yang pertama, dan ini menjadi /bin/bash
-o pipefail
/tmp/ff
, alih-alih parsing yang diperlukan /bin/bash
-o
pipefail
/tmp/ff
- getopt
(atau serupa) menggunakan optarg
, yang merupakan item berikutnya ARGV
, sebagai argumen untuk -o
, jadi gagal. Jika Anda membuat pembungkus (katakan, bash-pf
itu baru saja dilakukan exec /bin/bash -o pipefail "$@"
, dan letakkan di #!
telepon, itu akan berhasil. Lihat juga: en.wikipedia.org/wiki/Shebang_%28Unix%29
Apa yang saya lakukan jika memungkinkan adalah untuk memberi makan kode keluar dari foo
ke bar
. Misalnya, jika saya tahu bahwa foo
tidak pernah menghasilkan garis hanya dengan angka, maka saya bisa menempelkan kode keluar:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
Atau jika saya tahu bahwa output dari foo
tidak pernah berisi baris hanya dengan .
:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
Ini selalu bisa dilakukan jika ada beberapa cara bar
untuk bekerja pada semua kecuali baris terakhir, dan meneruskan baris terakhir sebagai kode keluarnya.
Jika bar
merupakan pipeline kompleks yang outputnya tidak Anda butuhkan, Anda dapat mem-bypass sebagiannya dengan mencetak kode keluar pada deskriptor file yang berbeda.
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
Setelah ini $exit_codes
biasanya foo:X bar:Y
, tetapi bisa jadi bar:Y foo:X
jika bar
berhenti sebelum membaca semua inputnya atau jika Anda beruntung. Saya pikir menulis ke pipa hingga 512 byte adalah atom pada semua unix, sehingga bagian foo:$?
- bar:$?
bagiannya tidak akan dicampur selama tag string berada di bawah 507 byte.
Jika Anda perlu mengambil output dari bar
, itu menjadi sulit. Anda dapat menggabungkan teknik-teknik di atas dengan mengatur agar bar
tidak pernah mengandung garis yang terlihat seperti indikasi kode keluar, tetapi ia mendapatkan fiddly.
output=$(echo;
{ { foo; echo foo:"$?" >&3; } |
{ bar | sed 's/^/^/'; echo bar:"$?" >&3; }
} 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')
Dan, tentu saja, ada opsi sederhana menggunakan file sementara untuk menyimpan status. Sederhana, tapi tidak yang sederhana dalam produksi:
/tmp
adalah satu-satunya tempat di mana skrip pasti dapat menulis file. Gunakan mktemp
, yang bukan POSIX tetapi tersedia di semua unit serius saat ini.foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
Mulai dari pipa:
foo | bar | baz
Berikut ini adalah solusi umum menggunakan hanya shell POSIX dan tidak ada file sementara:
exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
(bar || echo "1:$?" >&3) |
(baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-
$error_statuses
berisi kode status dari setiap proses yang gagal, dalam urutan acak, dengan indeks untuk mengetahui perintah mana yang dipancarkan setiap status.
# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2
# test if all commands succeeded:
test -z "$error_statuses"
# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null
Catat kutipan di $error_statuses
dalam tes saya; tanpa mereka grep
tidak dapat dibedakan karena baris baru dipaksa ke ruang.
Jadi saya ingin menyumbangkan jawaban seperti lesmana, tetapi saya pikir jawaban saya mungkin sedikit lebih sederhana dan sedikit lebih menguntungkan dari solusi Bourne-shell:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Saya pikir ini paling baik dijelaskan dari dalam ke luar - command1 akan mengeksekusi dan mencetak output regulernya di stdout (file descriptor 1), kemudian setelah selesai, printf akan mengeksekusi dan mencetak kode keluar command1 pada stdout, tetapi stdout diarahkan ke deskriptor file 3.
Ketika command1 sedang berjalan, stdout-nya sedang dipipakan ke command2 (output printf tidak pernah membuatnya ke command2 karena kita mengirimkannya ke file descriptor 3, bukan 1, yang dibaca oleh pipa). Kemudian kita mengarahkan output command2 ke file descriptor 4, sehingga ia juga tetap keluar dari file descriptor 1 - karena kita ingin file descriptor 1 gratis sebentar kemudian, karena kita akan membawa output printf pada file deskriptor 3 kembali ke file descriptor 1 - karena itulah substitusi perintah (backticks), akan menangkap dan itulah yang akan ditempatkan ke dalam variabel.
Bit terakhir sihir adalah yang pertama exec 4>&1
kita lakukan sebagai perintah terpisah - itu membuka file descriptor 4 sebagai salinan stdout shell eksternal. Substitusi perintah akan menangkap apa pun yang tertulis pada standar keluar dari perspektif perintah di dalamnya - tetapi, karena output command2 akan mengajukan deskriptor 4 sejauh menyangkut substitusi perintah, substitusi perintah tidak menangkapnya - namun, setelah "keluar" dari substitusi perintah, itu secara efektif masih pergi ke file deskriptor keseluruhan skrip 1.
(Itu exec 4>&1
harus menjadi perintah terpisah karena banyak shell umum tidak suka ketika Anda mencoba menulis ke deskriptor file di dalam substitusi perintah, yang dibuka di perintah "eksternal" yang menggunakan substitusi. Jadi ini adalah cara portabel paling sederhana untuk melakukannya.)
Anda dapat melihatnya dengan cara yang kurang teknis dan lebih lucu, seolah-olah output dari perintah saling melompati satu sama lain: command1 pipa ke command2, maka output printf melompati perintah 2 sehingga perintah2 tidak menangkapnya, dan kemudian output perintah 2 melompati dan keluar dari substitusi perintah sama seperti printf mendarat tepat pada waktunya untuk ditangkap oleh substitusi sehingga berakhir di variabel, dan output command2 berjalan dengan cara yang menyenangkan ditulis ke output standar, sama seperti dalam pipa normal.
Juga, seperti yang saya mengerti, $?
masih akan berisi kode kembali dari perintah kedua di dalam pipa, karena penugasan variabel, penggantian perintah, dan perintah majemuk semuanya secara efektif transparan ke kode pengembalian dari perintah di dalamnya, sehingga status pengembalian dari command2 harus disebarkan - ini, dan tidak harus mendefinisikan fungsi tambahan, itulah mengapa saya pikir ini mungkin solusi yang agak lebih baik daripada yang diusulkan oleh lesmana.
Menurut peringatan lesmana, ada kemungkinan bahwa command1 pada akhirnya akan menggunakan deskriptor file 3 atau 4, jadi agar lebih kuat, Anda akan melakukan:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Perhatikan bahwa saya menggunakan perintah majemuk dalam contoh saya, tetapi subkulit (menggunakan ( )
alih-alih { }
juga akan berfungsi, meskipun mungkin kurang efisien.)
Perintah mewarisi deskriptor file dari proses yang meluncurkannya, sehingga seluruh baris kedua akan mewarisi deskriptor file empat, dan perintah majemuk diikuti oleh 3>&1
akan mewarisi deskriptor file tiga. Jadi 4>&-
memastikan bahwa perintah inner compound tidak akan mewarisi file descriptor empat, dan 3>&-
tidak akan mewarisi file deskriptor tiga, jadi command1 mendapat lingkungan yang lebih bersih dan lebih standar. Anda juga bisa memindahkan bagian dalam 4>&-
ke sebelah 3>&-
, tapi saya pikir mengapa tidak membatasi ruang lingkupnya sebanyak mungkin.
Saya tidak yakin seberapa sering hal-hal menggunakan deskriptor file tiga dan empat secara langsung - Saya pikir sebagian besar program waktu menggunakan syscalls yang mengembalikan deskriptor file yang tidak digunakan saat ini, tetapi kadang-kadang kode menulis ke deskriptor file 3 secara langsung, saya tebak (saya bisa membayangkan sebuah program memeriksa deskriptor file untuk melihat apakah itu terbuka, dan menggunakannya jika ada, atau berperilaku berbeda sesuai jika tidak). Jadi yang terakhir mungkin terbaik untuk diingat dan digunakan untuk kasus-kasus tujuan umum.
-bash: 3: Bad file descriptor
.
Jika Anda menginstal paket moreutils, Anda dapat menggunakan utilitas mispipe yang melakukan persis seperti yang Anda minta.
solusi lesmana di atas juga dapat dilakukan tanpa overhead memulai subproses bersarang dengan menggunakan { .. }
sebagai gantinya (mengingat bahwa bentuk perintah yang dikelompokkan ini selalu harus diakhiri dengan titik koma). Sesuatu seperti ini:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
Saya telah memeriksa konstruk ini dengan versi dash 0.5.5 dan versi bash 3.2.25 dan 4.2.42, jadi walaupun beberapa shell tidak mendukung { .. }
pengelompokan, itu masih sesuai dengan POSIX.
set -o pipefail
di ksh atau sejumlah wait
perintah yang dipercikkan di salah satu. Saya pikir itu mungkin, sebagian setidaknya, menjadi masalah parsing untuk ksh, seolah-olah saya tetap menggunakan subshell maka itu berfungsi dengan baik, tetapi bahkan dengan if
memilih varian subkulit untuk ksh tetapi meninggalkan perintah majemuk untuk orang lain, itu gagal .
Ini portabel, yaitu berfungsi dengan shell yang sesuai dengan POSIX, tidak mengharuskan direktori saat ini dapat ditulis dan memungkinkan banyak skrip menggunakan trik yang sama untuk dijalankan secara bersamaan.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Sunting: ini adalah versi yang lebih kuat mengikuti komentar Gilles:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Sunting2: dan di sini ada varian yang sedikit lebih ringan mengikuti komentar dubiousjim:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
. @ Johan: Saya setuju itu lebih mudah dengan Bash tetapi dalam beberapa konteks, mengetahui bagaimana menghindari Bash sepadan.
Mengikuti ini dimaksudkan sebagai tambahan untuk jawaban @ Patrik, jika Anda tidak dapat menggunakan salah satu solusi umum.
Jawaban ini mengasumsikan sebagai berikut:
$PIPESTATUS
atau tidakset -o pipefail
Asumsi tambahan. Anda dapat menyingkirkan semuanya, tetapi resep ini terlalu banyak, sehingga tidak dibahas di sini:
- Yang ingin Anda ketahui adalah bahwa semua perintah di PIPE memiliki kode keluar 0.
- Anda tidak memerlukan informasi band samping tambahan.
- Shell Anda memang menunggu semua perintah pipa untuk kembali.
Sebelum:, foo | bar | baz
namun ini hanya mengembalikan kode keluar dari perintah terakhir ( baz
)
Dicari: $?
tidak boleh 0
(benar), jika ada perintah di pipa gagal
Setelah:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
# $? now is 0 only if all commands had exit code 0
Dijelaskan:
mktemp
. Ini biasanya segera membuat file dalam/tmp
wait
diperlukan untuk ksh
, karena ksh
yang lain tidak menunggu semua pipa perintah untuk menyelesaikan. Namun harap dicatat bahwa ada efek samping yang tidak diinginkan jika ada tugas latar belakang, jadi saya berkomentar secara default. Jika menunggu tidak sakit, Anda dapat berkomentar.read
kembali false
, maka true
tunjukkan kesalahanIni dapat digunakan sebagai pengganti plugin untuk satu perintah dan hanya perlu mengikuti:
/proc/fd/N
Bug:
Script ini memiliki bug jika /tmp
kehabisan ruang. Jika Anda memerlukan perlindungan terhadap kasing buatan ini, Anda juga dapat melakukannya sebagai berikut, namun ini memiliki kekurangan, bahwa jumlah 0
in 000
tergantung pada jumlah perintah dalam pipa, sehingga sedikit lebih rumit:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
Catatan portablility:
ksh
dan shell serupa yang hanya menunggu perintah pipa terakhir perlu wait
uncommented
Contoh terakhir menggunakan printf "%1s" "$?"
bukan echo -n "$?"
karena ini lebih portabel. Tidak semua platform mengartikan -n
dengan benar.
printf "$?"
akan melakukannya juga, namun printf "%1s"
menangkap beberapa kasus sudut jika Anda menjalankan skrip pada beberapa platform yang benar-benar rusak. (Baca: jika Anda kebetulan memprogram masuk paranoia_mode=extreme
.)
FD 8 dan FD 9 dapat lebih tinggi pada platform yang mendukung banyak digit. SETELAH shell konforman POSIX hanya perlu mendukung satu digit.
Diuji dengan Debian 8.2 sh
, bash
, ksh
, ash
, sash
dan bahkancsh
Dengan sedikit tindakan pencegahan, ini akan berhasil:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
Blok 'jika' berikut ini akan berjalan hanya jika 'perintah' berhasil:
if command; then
# ...
fi
Secara khusus, Anda dapat menjalankan sesuatu seperti ini:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
Yang akan menjalankan haconf -makerw
dan menyimpan stdout dan stderr ke "$ haconf_out". Jika nilai yang dikembalikan dari haconf
benar, maka blok 'jika' akan dieksekusi dan grep
akan membaca "$ haconf_out", mencoba mencocokkannya dengan "Cluster sudah dapat ditulisi".
Perhatikan bahwa pipa secara otomatis membersihkan diri mereka sendiri; dengan pengalihan Anda harus berhati-hati untuk menghapus "$ haconf_out" saat selesai.
Bukan sebagai elegan pipefail
, tetapi alternatif yang sah jika fungsi ini tidak dalam jangkauan.
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"
exec 8>&- 9>&-
{
{
{
{ #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8 # use exactly 1x prior to #END
#END
} 2>&1 | ${TEE} 1>&9
} 8>&1
} | exit $(read; printf "${REPLY}")
} 9>&1
exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
(Dengan bash setidaknya) dikombinasikan dengan set -e
satu dapat menggunakan subkulit untuk secara eksplisit meniru pipefail dan keluar pada kesalahan pipa
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
Jadi jika foo
gagal karena beberapa alasan - sisa program tidak akan dieksekusi dan skrip keluar dengan kode kesalahan yang sesuai. (Ini mengasumsikan bahwa foo
mencetak kesalahannya sendiri, yang cukup untuk memahami alasan kegagalan)
EDIT : Jawaban ini salah, tetapi menarik, jadi saya akan meninggalkannya untuk referensi di masa mendatang.
!
ke perintah membalikkan kode kembali.
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls
, bukan membalikkan kode keluarbogus_command