Saat ini saya memiliki skrip yang melakukan sesuatu seperti
./a | ./b | ./c
Saya ingin memodifikasinya sehingga jika salah satu dari a, b, atau c keluar dengan kode kesalahan, saya mencetak pesan kesalahan dan berhenti alih-alih meneruskan keluaran yang buruk.
Apa cara termudah / terbersih untuk melakukannya?
shell
error-handling
pipe
hugomg
sumber
sumber
&&|
yang berarti "hanya lanjutkan pipa jika perintah sebelumnya berhasil". Saya kira Anda juga bisa memiliki|||
yang berarti "lanjutkan pipa jika perintah sebelumnya gagal" (dan mungkin menyalurkan pesan kesalahan seperti Bash 4|&
).a
,b
,c
perintah tidak dijalankan secara berurutan tetapi secara paralel. Dengan kata lain, data mengalir secara berurutan daria
kec
, tetapi perintah aktuala
,b
danc
dimulai (secara kasar) pada waktu yang sama.Jawaban:
Jika Anda benar-benar tidak ingin perintah kedua dilanjutkan hingga perintah pertama berhasil, Anda mungkin perlu menggunakan file sementara. Versi sederhananya adalah:
Pengalihan '1> & 2' juga dapat disingkat '> & 2'; Namun, versi lama dari shell MKS salah menangani pengalihan kesalahan tanpa '1' sebelumnya jadi saya telah menggunakan notasi yang tidak ambigu untuk keandalan selama berabad-abad.
Ini membocorkan file jika Anda mengganggu sesuatu. Pemrograman shell tahan bom (lebih atau kurang) menggunakan:
Baris perangkap pertama mengatakan 'jalankan perintah'
rm -f $tmp.[12]; exit 1
ketika salah satu sinyal 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE, atau 15 SIGTERM terjadi, atau 0 (ketika shell keluar karena alasan apa pun). Jika Anda menulis skrip shell, perangkap terakhir hanya perlu menghapus perangkap di 0, yang merupakan perangkap keluar shell (Anda dapat membiarkan sinyal lain di tempatnya karena prosesnya akan segera berakhir).Dalam pipeline asli, 'c' layak untuk membaca data dari 'b' sebelum 'a' selesai - ini biasanya diinginkan (ini memberikan banyak inti pekerjaan yang harus dilakukan, misalnya). Jika 'b' adalah fase 'sort', maka ini tidak akan berlaku - 'b' harus melihat semua inputnya sebelum dapat menghasilkan output apa pun.
Jika Anda ingin mendeteksi perintah mana yang gagal, Anda dapat menggunakan:
Ini sederhana dan simetris - sangat mudah untuk diperpanjang ke pipa 4-bagian atau N-bagian.
Eksperimen sederhana dengan 'set -e' tidak membantu.
sumber
mktemp
atautempfile
.trap
perlu diingat bahwa pengguna selalu dapat mengirimSIGKILL
ke suatu proses untuk segera menghentikannya, dalam hal ini jebakan Anda tidak akan berpengaruh. Hal yang sama berlaku saat sistem Anda mengalami pemadaman listrik. Saat membuat file sementara, pastikan untuk menggunakanmktemp
karena itu akan meletakkan file Anda di suatu tempat, di mana mereka dibersihkan setelah reboot (biasanya ke/tmp
).Di bash Anda dapat menggunakan
set -e
danset -o pipefail
di awal file Anda. Perintah berikutnya./a | ./b | ./c
akan gagal jika salah satu dari tiga skrip gagal. Kode pengembalian akan menjadi kode pengembalian dari skrip pertama yang gagal.Perhatikan bahwa
pipefail
tidak tersedia di sh standar .sumber
set
(untuk total 4 versi yang berbeda), dan lihat bagaimana hal itu mempengaruhi keluaran yang terakhirecho
.Anda juga dapat memeriksa
${PIPESTATUS[]}
array setelah eksekusi penuh, misalnya jika Anda menjalankan:Kemudian
${PIPESTATUS}
akan menjadi array kode kesalahan dari setiap perintah di pipa, jadi jika perintah tengah gagal,echo ${PIPESTATUS[@]}
akan berisi sesuatu seperti:dan sesuatu seperti ini dijalankan setelah perintah:
akan memungkinkan Anda untuk memeriksa bahwa semua perintah di pipa berhasil.
sumber
#!/bin/sh
, karena jikash
bukan bash, itu tidak akan berfungsi. (Mudah diperbaiki dengan mengingat untuk menggunakan#!/bin/bash
sebagai gantinya.)echo ${PIPESTATUS[@]} | grep -qE '^[0 ]+$'
- mengembalikan 0 jika$PIPESTATUS[@]
hanya berisi 0 dan spasi (jika semua perintah di pipa berhasil).command1 && command2 | command3
Jika salah satu dari mereka gagal solusi Anda mengembalikan bukan nol.Sayangnya, jawaban Johnathan membutuhkan file sementara dan jawaban dari Michel dan Imron membutuhkan bash (meskipun pertanyaan ini diberi tag shell). Seperti yang telah ditunjukkan oleh orang lain, tidak mungkin untuk membatalkan pipa sebelum proses selanjutnya dimulai. Semua proses dimulai sekaligus dan semua akan berjalan sebelum kesalahan dapat dikomunikasikan. Tapi judul pertanyaannya juga menanyakan tentang kode kesalahan. Ini dapat diambil dan diselidiki setelah pipa selesai untuk mencari tahu apakah ada proses yang terlibat gagal.
Berikut adalah solusi yang menangkap semua kesalahan dalam pipa dan tidak hanya kesalahan pada komponen terakhir. Jadi ini seperti pipefail bash, hanya lebih kuat dalam artian Anda dapat mengambil semua kode kesalahan.
Untuk mendeteksi apakah ada yang gagal,
echo
perintah mencetak pada kesalahan standar jika ada perintah yang gagal. Kemudian output kesalahan standar gabungan disimpan$res
dan diselidiki nanti. Ini juga mengapa kesalahan standar dari semua proses diarahkan ke keluaran standar. Anda juga dapat mengirim keluaran itu ke/dev/null
atau membiarkannya sebagai indikator lain bahwa ada yang tidak beres. Anda dapat mengganti pengalihan terakhir ke/dev/null
dengan file jika Anda tidak perlu menyimpan keluaran dari perintah terakhir di mana saja.Untuk bermain lebih banyak dengan konstruksi ini dan untuk meyakinkan diri sendiri bahwa ini benar-benar melakukan apa yang seharusnya, saya menggantinya
./a
,./b
dan./c
dengan subkulit yang mengeksekusiecho
,cat
danexit
. Anda dapat menggunakan ini untuk memeriksa bahwa konstruksi ini benar-benar meneruskan semua output dari satu proses ke proses lainnya dan kode kesalahan dicatat dengan benar.sumber