it-swarm-tr.com

Başka birine yönlendirilen işlemin çıkış durumunu alın

Bir boru ile bağlı iki işlem foo ve bar var:

$ foo | bar

bar her zaman 0'dan çıkar; foo çıkış koduyla ilgileniyorum. Ona ulaşmanın bir yolu var mı?

314
Michael Mrozek

bash kullanıyorsanız, boru hattının her öğesinin çıkış durumunu almak için PIPESTATUS dizi değişkenini kullanabilirsiniz.

$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0

zsh kullanıyorsanız, diziye pipestatus (büyük/küçük harf önemlidir!) Denir ve dizi endeksleri birde başlar:

$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0

Bunları bir işlev içinde değerleri kaybetmeyecek şekilde birleştirmek için:

$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0

Yukarıdakileri bash veya zsh ile çalıştırın ve aynı sonuçları elde edersiniz; retval_bash ve retval_zsh Ayarlanacak. Diğeri boş olacak. Bu, bir işlevin return $retval_bash $retval_zsh (tırnak eksikliğine dikkat edin!).

277
camh

Bunu yapmanın 3 yaygın yolu vardır:

Pipefail

İlk yol pipefail seçeneğini (ksh, zsh veya bash) ayarlamaktır. Bu en basit olanıdır ve temelde çıkış durumunu $? sıfırdan çıkmak için son programın çıkış koduna (veya hepsi başarıyla çıkarsa sıfır).

$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1

$ PIPESTATUS

Bash ayrıca $PIPESTATUS ($pipestatus in zsh), son ardışık düzendeki tüm programların çıkış durumunu içerir.

$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1

İhtiyacınız olan ardışık düzende belirli bir değeri almak için 3. komut örneğini kullanabilirsiniz.

Ayrı infazlar

Bu, çözümlerin en uygunsuzudur. Her komutu ayrı ayrı çalıştırın ve durumu yakalayın

$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
253
Patrick

Bu çözüm, bash'a özgü özellikler veya geçici dosyalar kullanmadan çalışır. Bonus: Sonunda çıkış durumu aslında bir çıkış durumudur ve dosyadaki bazı dizeler değildir.

Durum:

someprog | filter

someprog 'dan çıkış durumunu ve filter' den çıktı almak istiyorsunuz.

İşte benim çözümüm:

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

bu yapının sonucu yapının stdoutu olarak filter 'dan stdout ve yapının çıkış durumu olarak someprog' dan çıkış durumu.


bu yapı ayrıca {...} alt kabukları yerine (...) basit komut gruplamasıyla da çalışır. alt kabukların bazı sonuçları vardır, diğerleri arasında burada ihtiyacımız olmayan bir performans maliyeti. daha fazla ayrıntı için ince bash kılavuzunu okuyun: 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

Ne yazık ki bash dilbilgisi, kıvırcık parantezler için boşluklar ve noktalı virgüller gerektirir, böylece yapı çok daha geniş hale gelir.

Bu metnin geri kalanı için subshell varyantını kullanacağım.


Örnek someprog ve 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 $?

Örnek çıktı:

filtered line1
filtered line2
filtered line3
42

Not: alt işlem, açık dosya tanımlayıcılarını üst öğeden devralır. Yani someprog açık dosya tanımlayıcı 3 ve 4'ü devralır. someprog dosya tanımlayıcı 3'e yazarsa, çıkış durumu olur. read yalnızca bir kez okunduğu için gerçek çıkış durumu yok sayılır.

someprog dosyanızın dosya tanımlayıcı 3 veya 4'e yazabileceğinden endişe ediyorsanız, someprog çağrılmadan önce dosya tanımlayıcıları kapatmak en iyisidir.

(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

someprog öncesi exec 3>&- 4>&-, someprog yürütülmeden önce dosya tanımlayıcıyı kapatır, böylece someprog için bu dosya tanımlayıcıları yoktur.

Ayrıca şu şekilde de yazılabilir: someprog 3>&- 4>&-


Yapının adım adım açıklaması:

( ( ( ( someprog;          #part6
        echo $? >&3        #part5
      ) | filter >&4       #part4
    ) 3>&1                 #part3
  ) | (read xs; exit $xs)  #part2
) 4>&1                     #part1

Aşağıdan yukarıya:

  1. Stdout'a yönlendirilen dosya tanımlayıcı 4 ile bir alt kabuk oluşturulur. Bu, alt kabuktaki dosya tanımlayıcısına 4 yazdırılan her şeyin, tüm yapının stdout'u olacağı anlamına gelir.
  2. Bir kanal oluşturulur ve soldaki (#part3) Ve sağdaki (#part2) Komutlar yürütülür. exit $xs Aynı zamanda borunun son komutudur ve bu da stdin dizesinin tüm yapının çıkış durumu olacağı anlamına gelir.
  3. Stdout'a yönlendirilen dosya tanımlayıcı 3 ile bir alt kabuk oluşturulur. Bu, bu alt kabuktaki dosya tanımlayıcısına 3 yazdırılan her şeyin #part2 İle sonuçlanacağı ve tüm yapının çıkış durumu olacağı anlamına gelir.
  4. Bir kanal oluşturulur ve soldaki komutlar (#part5 Ve #part6) Ve sağda (filter >&4) Yürütülür. filter çıktısı dosya tanımlayıcı 4'e yönlendirilir. #part1 Dosya tanımlayıcı 4 stdout'a yönlendirildi. Bu, filter çıktısının tüm yapının stdout'u olduğu anlamına gelir.
  5. #part6 'Dan çıkış durumu dosya tanımlayıcı 3'e yazdırılır. #part3 Dosya tanımlayıcı 3'te #part2 Yönlendirildi. Bu, #part6 'Dan çıkış durumunun tüm yapı için son çıkış durumu olacağı anlamına gelir.
  6. someprog yürütülür. Çıkış durumu #part5 İçinde alınır. Stdout, boru tarafından #part4 İçinde alınır ve filter öğesine iletilir. filter çıktısı #part4 'Da açıklandığı gibi stdout'a ulaşacak
58
lesmana

Sorduğunuz şey tam olarak olmasa da,

#!/bin/bash -o pipefail

böylece borularınız sıfır olmayan son dönüşü döndürür.

biraz daha az kodlama olabilir

Düzenle: Örnek

[[email protected] ~]# false | true
[[email protected] ~]# echo $?
0
[[email protected] ~]# set -o pipefail
[[email protected] ~]# false | true
[[email protected] ~]# echo $?
1
37
Chris

Mümkün olduğunda, çıkış kodunu foo'den bar içine beslemektir. Örneğin, foo 'ın asla sadece rakam içeren bir satır üretmediğini biliyorsanız, sadece çıkış koduna yapışabilirim:

{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'

Veya foo çıktısının asla .:

{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'

Bu, bar öğesinin son satır dışındaki tüm öğeler üzerinde çalışmasını sağlamanın ve son satırı çıkış kodu olarak iletmenin bir yolu varsa her zaman yapılabilir.

bar çıktısına ihtiyacınız olmayan karmaşık bir ardışık düzen ise, çıkış kodunu farklı bir dosya tanımlayıcıya yazdırarak bir kısmını atlayabilirsiniz.

exit_codes=$({ { foo; echo foo:"$?" >&3; } |
               { bar >/dev/null; echo bar:"$?" >&3; }
             } 3>&1)

Bundan sonra $exit_codes genellikle foo:X bar:Y, ancak bar:Y foo:X _ bar tüm girdilerini okumadan önce veya şanssızsanız. Bence 512 bayta kadar borulara yazıyor tüm birleşimlerde atomik, bu yüzden foo:$? ve bar:$? parçaları, etiket dizeleri 507 baytın altında olduğu sürece karıştırılmaz.

bar çıkışını yakalamanız gerekiyorsa zorlaşır. Hiçbir zaman bir çıkış kodu göstergesine benzeyen bir satır içermemek için bar çıkışını ayarlayarak yukarıdaki teknikleri bir araya getirebilirsiniz, ancak fiddly alır.

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')

Ve elbette, durumu saklamak için geçici bir dosya kullanarak gibi basit bir seçenek var. Basit, ama değil üretimde basit:

  • Eşzamanlı olarak çalışan birden çok komut dosyası varsa veya aynı komut dosyası bu yöntemi birkaç yerde kullanıyorsa, bunların farklı geçici dosya adları kullandığından emin olmanız gerekir.
  • Paylaşılan bir dizinde güvenli bir şekilde geçici dosya oluşturmak zordur. Sıklıkla, /tmp, betiğin dosyaları yazabileceğinden emin olduğu tek yerdir. mktemp kullanın, ki bu POSIX değil, günümüzde tüm ciddi birliklerde kullanılabilir.
foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")

Boru hattından başlayarak:

foo | bar | baz

Sadece POSIX Kabuğu kullanan ve geçici dosyaları olmayan genel bir çözüm:

exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
        (bar || echo "1:$?" >&3) | 
        (baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-

$error_statuses, başarısız olan işlemlerin durum kodlarını, rasgele sırayla, her bir durumun hangi komutun yaydığını bildiren dizinlerle birlikte içerir.

# 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

$error_statuses testlerimde; onlar olmadan grep farklılaşamaz çünkü yeni satırlar boşluklara zorlanır.

17
Jander

Bu yüzden lesmana gibi bir cevaba katkıda bulunmak istedim, ama benimkinin belki biraz daha basit ve biraz daha avantajlı saf Bourne-Shell çözümü olduğunu düşünüyorum:

# 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.

Bence bu en iyisi içten dışa açıklanır - command1 stdout'ta (dosya tanımlayıcı 1) normal çıktısını yürütür ve yazdırır, sonra bir kez yapılır, printf stdout'ta command1'in çıkış kodunu çalıştırır ve yazdırır, ancak bu stdout yönlendirilir dosya tanıtıcı 3.

Komut1 çalışırken, stdout'u command2'ye bağlanır (printf'in çıkışı asla command2'ye gönderilmez çünkü pipo'nun okuduğu 1 yerine dosya tanımlayıcı 3'e göndeririz). Daha sonra command2'nin çıktısını dosya tanımlayıcı 4'e yönlendiririz, böylece dosya tanımlayıcı 1'in dışında kalır - çünkü dosya tanımlayıcı 1'in biraz sonra ücretsiz olmasını istiyoruz, çünkü dosya tanımlayıcı 3'teki printf çıktısını dosya tanımlayıcıya geri getireceğiz 1 - çünkü komut ikamesi (backticks) bu, yakalayacaktır ve değişkene yerleştirilecek olan budur.

Sihrin son kısmı ilk exec 4>&1 ayrı bir komut olarak yaptık - dosya tanımlayıcı 4'ü harici Shell'in stdout'unun bir kopyası olarak açar. Komut ikamesi, standartta yazılanları içindeki komutların perspektifinden yakalar - ancak komut2'nin çıkışı komut ikamesi söz konusu olduğunda tanımlayıcı 4'e dosyalayacağından, komut ikamesi onu yakalamaz - ancak, bir kez komut ikamesi "dışarı" alırsa, etkili bir şekilde hala komut dosyasının genel dosya tanımlayıcı 1 gidiyor.

(exec 4>&1 ayrı bir komut olmalıdır çünkü birçok ortak kabuk, ikame kullanan "harici" komutta açılan, bir komut ikamesi içindeki bir dosya tanımlayıcısına yazmaya çalıştığınızda hoşlanmıyor. Bu, bunu yapmanın en basit taşınabilir yoludur.)

Komutların çıktıları birbirini atlıyormuş gibi ona daha az teknik ve daha eğlenceli bir şekilde bakabilirsiniz: komut1 komutları komut2'ye atlar, ardından printf'in çıkışı komut 2'nin üzerine atlar, böylece komut2 yakalamaz ve sonra komut 2'nin çıktısı, tıpkı printf değişkenle yakalanacak şekilde ikame tarafından yakalanmak için tam zamanında geldiği gibi komut ikamesinin üstünden ve dışına atlar ve komut2'nin çıktısı, standart çıktıya yazıldığı gibi neşeli yoluna devam eder. normal bir boruda.

Ayrıca, anladığım kadarıyla, $?, borudaki ikinci komutun dönüş kodunu yine de içerecektir, çünkü değişken atamaları, komut ikameleri ve bileşik komutların tümü, içindeki komutun dönüş koduna etkili bir şekilde şeffaf olduğundan, komut2'nin dönüş durumunun yayılması gerekir - bu ve ek bir işlev tanımlamak zorunda değilim, bu yüzden lesmana'nın önerdiğinden biraz daha iyi bir çözüm olabileceğini düşünüyorum.

Uyarılar lesmana'dan bahsetmişken, command1'in bir noktada 3 veya 4 dosya tanımlayıcılarını kullanması mümkündür, böylece daha sağlam olmak için şunları yaparsınız:

exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-

Örneğimde bileşik komutları, ancak alt kabukları (( ) onun yerine { } de çalışacaktır, ancak belki daha az verimli olabilir.)

Komutlar, dosya tanımlayıcılarını başlatan işlemden devralır, böylece ikinci satırın tamamı dosya tanımlayıcı dörtünü ve bileşik komutun ardından 3>&1 üç dosya tanımlayıcısını devralır. Böylece 4>&-, iç bileşik komutunun dosya tanımlayıcı dördünü devralmayacağını ve 3>&- üç dosya tanımlayıcısını devralmaz, bu yüzden command1 'daha temiz', daha standart bir ortam elde eder. Ayrıca içteki 4>&- yanında 3>&-, ama neden sadece kapsamını mümkün olduğunca sınırlamak değil.

Ne sıklıkta şeyler dosya tanımlayıcı üç ve dört doğrudan kullandığınızdan emin değilim - çoğu zaman programlar, şu anda kullanılmayan dosya tanımlayıcıları döndüren syscalls kullandığını düşünüyorum, ancak bazen kod doğrudan dosya tanımlayıcı 3'e yazar, ben tahmin (Ben bir program açık olup olmadığını görmek için bir dosya tanımlayıcı kontrol ve eğer açıksa onu kullanarak, ya da değilse farklı davranır hayal). Bu nedenle, ikincisi akılda tutmak ve genel amaçlı vakalar için kullanmak en iyisidir.

12
mtraceur

moreutils paketini yüklediyseniz, tam olarak sorduğunuz şeyi yapan yanlış borusu yardımcı programını kullanabilirsiniz.

11
Emanuele Aina

lesmana'nın yukarıdaki çözümü, bunun yerine { .. } kullanarak yuvalanmış alt süreçlerin başlatılması yükü olmadan da yapılabilir (bu gruplandırılmış komutların formunun her zaman noktalı virgülle bitmesi gerektiğini hatırlamak). Bunun gibi bir şey:

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1

Bu yapıyı tire sürümü 0.5.5 ve bash sürümleri 3.2.25 ve 4.2.42 ile kontrol ettim, bu yüzden bazı kabuklar { .. } Gruplamayı desteklemese bile, hala POSIX uyumlu.

7
pkeller

Aşağıda, ortak çözümlerden birini kullanamamanız durumunda @Patrik'in cevabına bir ek olarak kastedilmektedir.

Bu cevap aşağıdakileri varsayar:

  • $PIPESTATUS Veya set -o pipefail Bilmeyen bir Kabuğunuz var
  • Paralel yürütme için bir kanal kullanmak istiyorsunuz, bu nedenle geçici dosyalar yok.
  • Senaryoyu kesintiye uğratırsanız, muhtemelen ani bir elektrik kesintisi nedeniyle ek karışıklık istemezsiniz.
  • Bu çözümün izlenmesi nispeten kolay olmalı ve okunması temiz olmalıdır.
  • Ek alt kabuklar tanıtmak istemezsiniz.
  • Mevcut dosya tanımlayıcılarıyla uğraşamazsınız, bu nedenle stdin/out/err öğesine dokunulmamalıdır (ancak geçici olarak yeni bir tanesini tanıtabilirsiniz)

Ek varsayımlar. Her şeyden kurtulabilirsiniz, ancak bu tarifi çok fazla zorlar, bu yüzden burada ele alınmaz:

  • Bilmek istediğiniz tek şey, PIPE içindeki tüm komutların çıkış kodu 0 olmasıdır.
  • Ek yan bant bilgilerine ihtiyacınız yoktur.
  • Kabuğunuz tüm kanal komutlarının geri dönmesini bekler.

Önce: foo | bar | baz, Ancak bu yalnızca son komutun çıkış kodunu döndürür (baz)

İstenen: Borudaki komutlardan herhangi biri başarısız olursa $?0 (True) olmamalıdır

Sonra:

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

Açıklaması:

  • mktemp ile bir geçici dosya oluşturulur. Bu genellikle hemen /tmp Dosyasında bir dosya oluşturur
  • Bu geçici dosya daha sonra yazma için FD 9'a ve okuma için FD 8'e yönlendirilir
  • Ardından geçici dosya hemen silinir. Bununla birlikte, her iki FD de var olana kadar açık kalır.
  • Şimdi boru başlatıldı. Her adım yalnızca bir hata olması durumunda FD 9'a eklenir.
  • waitksh için gereklidir, çünkü ksh else tüm kanal komutlarının bitmesini beklemez. Ancak, bazı arka plan görevleri varsa istenmeyen yan etkiler olduğunu lütfen unutmayın, bu yüzden varsayılan olarak yorum yaptım. Bekleme zarar vermezse, yorum yapabilirsiniz.
  • Daha sonra dosyanın içeriği okunur. Boşsa (hepsi işe yaradığı için) readfalse döndürür, bu nedenle true bir hatayı gösterir

Bu, tek bir komut için eklenti değiştirme olarak kullanılabilir ve yalnızca aşağıdakilere ihtiyaç duyar:

  • Kullanılmayan FD 9 ve 8
  • Geçici dosyanın adını tutacak tek bir ortam değişkeni
  • Ve bu tarif, IO yeniden yönlendirmeye izin veren herhangi bir Kabuğa uyarlanabilir)
  • Ayrıca oldukça platform agnostik ve /proc/fd/N

Bug:

Bu komut dosyasında /tmp Alanın bitmesi durumunda bir hata var. Bu yapay kasaya karşı da korunmaya ihtiyacınız varsa, bunu şu şekilde yapabilirsiniz, ancak bunun dezavantajı vardır, 0 İçindeki 000 Sayısının borudaki komutların sayısına bağlı olması , bu yüzden biraz daha karmaşık:

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"

Taşınabilirlik notları:

  • ksh ve yalnızca son boru komutunu bekleyen benzer mermilerin wait önerilmemiş olması gerekir

  • Son örnek, daha taşınabilir olduğu için printf "%1s" "$?" Yerine echo -n "$?" Kullanır. Her platform -n Öğesini doğru yorumlamaz.

  • printf "$?" Bunu da yapar, ancak printf "%1s" Komut dosyasını gerçekten kırık bir platformda çalıştırmanız durumunda bazı köşe vakalarını yakalar. (Okuyun: paranoia_mode=extreme Programında bulunuyorsanız.)

  • FD 8 ve FD 9, birden fazla rakamı destekleyen platformlarda daha yüksek olabilir. POSIX uyumlu bir kabuk AFAIR sadece tek basamakları desteklemelidir.

  • Debian 8.2 sh, bash, ksh, ash, sash ve hatta csh ile test edildi

5
Tino

Bu taşınabilir, yani herhangi bir POSIX uyumlu Shell ile çalışır, geçerli dizinin yazılabilir olmasını gerektirmez ve aynı hileyi kullanan birden fazla komut dosyasının aynı anda çalışmasına izin verir.

(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))

Düzenleme: İşte Gilles'in yorumlarını takip eden daha güçlü bir sürüm:

(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))

Edit2: ve burada dubiousjim yorumunu takiben biraz daha hafif bir varyant:

(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
4
jlliagre

Biraz önlemle, bu işe yaramalıdır:

foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
3
alex

Aşağıdaki 'if' bloğu yalnızca 'command' başarılı olduğunda çalışır:

if command; then
   # ...
fi

Özellikle, şöyle bir şey çalıştırabilirsiniz:

haconf_out=/path/to/some/temporary/file

if haconf -makerw > "$haconf_out" 2>&1; then
   grep -iq "Cluster already writable" "$haconf_out"
   # ...
fi

Hangisi haconf -makerw ve stdout ve stderr "$ haconf_out" için saklayın. haconf öğesinden döndürülen değer true olursa, 'if' bloğu yürütülür ve grep "$ haconf_out" ifadesini okuyarak "Küme zaten yazılabilir" ile eşleştirmeye çalışır.

Boruların kendilerini otomatik olarak temizlediğine dikkat edin; yeniden yönlendirme ile "$ haconf_out" işlemini tamamladığınızda kaldırmanız için dikkatli olmanız gerekir.

pipefail kadar zarif değil, ancak bu işlev erişilemiyorsa meşru bir alternatif.

2
Rany Albeg Wein
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
$
1
C.G.

(En azından bash ile) set -e boru hattını açıkça taklit etmek ve boru hatasından çıkmak için alt kabuk kullanabilir

set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program

Eğer foo herhangi bir nedenle başarısız olursa - programın geri kalanı yürütülmez ve komut dosyası ilgili hata koduyla çıkar. (Bu, foo değerinin kendi hatasını yazdırdığını varsayar; bu da hatanın nedenini anlamak için yeterlidir)

0
noonex