Mengatur pipefail untuk satu perintah pipa

18

Saya perlu menjalankan sejumlah perintah shell yang disalurkan dari skrip non-BASH (yaitu skrip PHP) seperti ini:

command1 | command2 | command3

sehingga, jika command1gagal dengan kode keluar bukan nol, setiap perintah lainnya juga gagal. Sejauh ini, yang saya pikirkan adalah:

set -o pipefail && command1 | command2 | command3

Tetapi meskipun berjalan dengan baik dari terminal, ia menghasilkan ini jika dieksekusi dari skrip:

sh: 1: set: Opsi ilegal -o pipefail

Desmond Hume
sumber
Tampaknya /bin/shtidak suka set -o pipefail. Apakah itu sebenarnya bashdalam penyamaran, atau apakah itu shell yang berbeda? Kapan bashdijalankan sebagai /bin/sh, apakah ia menerima set -o pipefail?
Jonathan Leffler
@ JonathanLeffler Ketika saya menjalankannya /bin/sh set -o pipefailmengatakan /bin/sh: 0: Can't open set(hal yang sama dengan sudo). Semoga saya mengujinya dengan benar. Sistemnya adalah Ubuntu 12.
Desmond Hume
Anda harus mencoba /bin/sh -c "set -o pipefail"; seolah-olah, shell sedang mencoba untuk mengeksekusi skrip di direktori saat ini dipanggil setdan tidak menemukannya.
Jonathan Leffler
@JonathanLeffler /bin/sh -c "set -o pipefail"tidak berfungsi, namun bash -c "set -o pipefail"tidak.
Desmond Hume
Jadi, masalah Anda adalah skrip dijalankan oleh /bin/shyang tidak dikenali set -o pipefail. Karenanya, Anda harus memastikan bahwa skrip dijalankan oleh /bin/bashalih-alih /bin/sh. Atau, jika Anda percaya diri, berani - dan mungkin bodoh - ubah /bin/shmenjadi tautan, atau salinan, /bin/bashalih-alih shell mana pun yang saat ini ditautkan atau salinannya. Jika Anda sedang yakin bahwa /bin/shadalah bash, maka Anda menggunakan perilaku yang bashtidak mengekspos ketika dijalankan sebagai /bin/sh; gunakan bashsebagai bash.
Jonathan Leffler

Jawaban:

18

Dari baris perintah Bash Anda perlu mengaktifkan subkulit untuk menghindari pipefailditetapkan setelahnya:

$ (set -o pipefail && command1 | command2 | command3)

Ini akan membatasi efek pipefailopsi ke subkulit yang dibuat oleh tanda kurung (...).

Contoh nyata:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

Jika Anda menggunakan panggilan shell eksplisit dengan -copsi Anda tidak perlu subkulit, baik dengan bashatau dengan shalias untuk bash:

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

Karena Anda shtidak menerima pipefailopsi, saya harus berasumsi bahwa itu adalah beberapa versi yang lebih tua atau yang dimodifikasi bash- atau itu sebenarnya beberapa shell lain sepenuhnya.

thkala
sumber
1
Apakah $di bagian pertama bagian dari perintah itu hanya prompt shell? Mencoba dua arah, tidak benar-benar berhasil. The bash -ccara selalu bekerja, tidak terlalu elegan tho ..
Desmond Hume
5

Tidak yakin mengapa itu tidak disebutkan di atas, tetapi dimungkinkan untuk menghapus secara eksplisit pipefail, menggunakan set +o pipefail.

set -o pipefail
command1 | command2 | command3
set +o pipefail

Jika Anda menjalankan fragmen, dan tidak yakin tentang pipefailset yang sudah ditetapkan, Anda dapat menggunakan ini dengan subkulit seperti yang disarankan sebelumnya:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )
robbat2
sumber
0

Anda dapat menggunakan $ (command1) yang dikombinasikan dengan $? :

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Mencetak "abc"

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Cetakan "".

"[$? -eq 0] &&" berarti bahwa perintah berikut dijalankan hanya jika yang sebelumnya berhasil.

Itu dosen tidak menjawab pertanyaan Anda, tetapi itu adalah solusi untuk masalah Anda.

cglacet
sumber
Apakah itu berarti bahwa saya akan selalu perlu menyimpan output dari masing-masing perintah ke dalam variabel?
Desmond Hume
Selalu saya tidak tahu tetapi dengan solusi saya ya Anda akan (jika Anda membutuhkannya untuk perintah Anda berikutnya). Kecuali Anda bisa menuliscommand1 && command2 && command3
cglacet