Apakah ada sesuatu yang mirip dengan pipefail untuk banyak perintah, seperti pernyataan 'coba' tetapi dalam bash. Saya ingin melakukan sesuatu seperti ini:
echo "trying stuff"
try {
command1
command2
command3
}
Dan pada titik mana pun, jika ada perintah gagal, putus dan gema kesalahan dari perintah itu. Saya tidak mau harus melakukan sesuatu seperti:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
Dan seterusnya ... atau yang seperti:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Karena argumen dari setiap perintah yang saya percayai (benar jika saya salah) akan saling mengganggu. Kedua metode ini nampaknya sangat bertele-tele dan tidak menyenangkan bagi saya, jadi saya di sini mencari metode yang lebih efisien.
set -euo pipefail
.set -e
adalah ide yang mengerikan . Lihat latihan di BashFAQ # 105 yang membahas beberapa kasus tepi tak terduga yang diperkenalkannya, dan / atau perbandingan yang menunjukkan ketidaksesuaian antara implementasi shell (dan versi shell) yang berbeda di in-ulm.de/ ~mascheck/various/set -e .Jawaban:
Anda dapat menulis fungsi yang meluncurkan dan menguji perintah untuk Anda. Asumsikan
command1
dancommand2
merupakan variabel lingkungan yang telah disetel ke perintah.sumber
$*
, itu akan gagal jika ada argumen di dalamnya; gunakan"$@"
saja. Demikian pula, letakkan$1
di dalam tanda kutip diecho
perintah.test
karena itu adalah perintah bawaan.ls
. Jika Anda memohonls foo
dan mendapatkan pesan kesalahan dari formulirls: foo: No such file or directory\n
Anda memahami masalahnya. Jika sebaliknya Anda membuatls: foo: No such file or directory\nerror with ls\n
Anda terganggu oleh informasi yang berlebihan. Dalam hal ini, Cukup mudah untuk berpendapat bahwa superfluitas itu sepele, tetapi dengan cepat tumbuh. Pesan kesalahan singkat itu penting. Tetapi yang lebih penting, jenis pembungkus ini mendorong penulis untuk sepenuhnya menghilangkan pesan kesalahan yang baik.Apa yang Anda maksud dengan "drop out and echo the error"? Jika Anda ingin skrip dihentikan segera setelah perintah gagal, lakukan saja
di awal skrip (tetapi perhatikan peringatan di bawah). Jangan repot-repot mengulangi pesan kesalahan: biarkan perintah yang gagal mengatasinya. Dengan kata lain, jika Anda melakukannya:
dan command2 gagal, saat mencetak pesan kesalahan ke stderr, maka tampaknya Anda telah mencapai apa yang Anda inginkan. (Kecuali saya salah menafsirkan apa yang Anda inginkan!)
Sebagai akibat wajar, perintah apa pun yang Anda tulis harus berperilaku baik: harus melaporkan kesalahan ke stderr alih-alih stdout (kode sampel dalam pertanyaan mencetak kesalahan ke stdout) dan harus keluar dengan status bukan nol ketika gagal.
Namun, saya tidak lagi menganggap ini sebagai praktik yang baik.
set -e
telah mengubah semantiknya dengan versi bash yang berbeda, dan meskipun itu berfungsi dengan baik untuk skrip sederhana, ada begitu banyak kasus tepi yang pada dasarnya tidak dapat digunakan. (Pertimbangkan hal-hal seperti:set -e; foo() { false; echo should not print; } ; foo && echo ok
Semantik di sini agak masuk akal, tetapi jika Anda mengubah kode menjadi fungsi yang bergantung pada pengaturan opsi untuk mengakhiri lebih awal, Anda dapat dengan mudah digigit.) IMO lebih baik untuk menulis:atau
sumber
trap some_func 0
akan dieksekusisome_func
saat keluar)|| exit
secara eksplisit setelah setiap perintah.Saya memiliki satu set fungsi scripting yang saya gunakan secara luas di sistem Red Hat saya. Mereka menggunakan fungsi sistem dari
/etc/init.d/functions
untuk mencetak hijau[ OK ]
dan merah[FAILED]
indikator status .Anda dapat mengatur
$LOG_STEPS
variabel ke nama file log jika Anda ingin mencatat perintah yang gagal.Pemakaian
Keluaran
Kode
sumber
Untuk apa nilainya, cara yang lebih singkat untuk menulis kode untuk memeriksa setiap perintah untuk sukses adalah:
Itu masih membosankan tetapi setidaknya itu bisa dibaca.
sumber
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Alternatifnya adalah dengan menggabungkan perintah bersama-sama
&&
sehingga yang pertama gagal mencegah sisanya dari mengeksekusi:Ini bukan sintaks yang Anda minta dalam pertanyaan, tetapi ini adalah pola umum untuk use case yang Anda jelaskan. Secara umum perintah harus bertanggung jawab atas kegagalan pencetakan sehingga Anda tidak harus melakukannya secara manual (mungkin dengan
-q
tanda untuk membungkam kesalahan saat Anda tidak menginginkannya). Jika Anda memiliki kemampuan untuk memodifikasi perintah-perintah ini, saya akan mengeditnya untuk berteriak pada kegagalan, daripada membungkusnya dengan sesuatu yang lain yang melakukannya.Perhatikan juga bahwa Anda tidak perlu melakukan:
Anda bisa mengatakan:
Dan ketika Anda lakukan perlu untuk memeriksa kembali kode menggunakan konteks aritmatika bukannya
[ ... -ne
:sumber
Alih-alih membuat fungsi runner atau menggunakan
set -e
, gunakantrap
:Perangkap bahkan memiliki akses ke nomor baris dan baris perintah dari perintah yang memicu itu. Variabelnya adalah
$BASH_LINENO
dan$BASH_COMMAND
.sumber
trap - ERR
untuk mematikan jebakan di akhir "blok".Secara pribadi saya lebih suka menggunakan pendekatan yang ringan, seperti yang terlihat di sini ;
Contoh penggunaan:
sumber
sumber
$*
, itu akan gagal jika ada argumen yang memiliki ruang di dalamnya; gunakan"$@"
saja. (Meskipun $ * tidak apa-apa dalamecho
perintah.)Saya telah mengembangkan implementasi try & catch yang hampir sempurna di bash, yang memungkinkan Anda menulis kode seperti:
Anda bahkan dapat membuat sarang blok uji coba di dalam diri mereka!
Kode ini adalah bagian dari bash boilerplate / framework saya . Lebih lanjut memperluas gagasan mencoba & menangkap dengan hal-hal seperti penanganan kesalahan dengan backtrace dan pengecualian (ditambah beberapa fitur bagus lainnya).
Berikut kode yang bertanggung jawab hanya untuk mencoba & menangkap:
Jangan ragu untuk menggunakan, garpu, dan berkontribusi - ini ada di GitHub .
sumber
Maaf saya tidak dapat membuat komentar pada jawaban pertama. Tetapi Anda harus menggunakan contoh baru untuk menjalankan perintah: cmd_output = $ ($ @)
sumber
Untuk cangkang ikan pengguna yang tersandung di utas ini.
Membiarkan
foo
menjadi fungsi yang tidak "mengembalikan" (gema) nilai, tetapi menetapkan kode keluar seperti biasa.Untuk menghindari memeriksa
$status
setelah memanggil fungsi, Anda dapat melakukan:Dan jika terlalu panjang untuk muat pada satu baris:
sumber
Ketika saya menggunakan
ssh
saya perlu membedakan antara masalah yang disebabkan oleh masalah koneksi dan kode kesalahan perintah jarak jauh dalam modeerrexit
(set -e
). Saya menggunakan fungsi berikut:sumber
Anda dapat menggunakan solusi luar biasa @ john-kugelman yang ditemukan di atas pada sistem non-RedHat dengan mengomentari baris ini dalam kodenya:
Kemudian, rekatkan kode di bawah ini di bagian akhir. Pengungkapan penuh: Ini hanya salinan & tempel langsung bit relevan dari file yang disebutkan di atas yang diambil dari Centos 7.
Diuji pada MacOS dan Ubuntu 18.04.
sumber
Memeriksa status secara fungsional
Pemakaian:
Keluaran
sumber