İki işlemim var foo
ve bar
bir boruya bağlı:
$ foo | bar
bar
daima 0'dan çıkar; Çıkış koduyla ilgileniyorum foo
. Buna ulaşmanın bir yolu var mı?
İki işlemim var foo
ve bar
bir boruya bağlı:
$ foo | bar
bar
daima 0'dan çıkar; Çıkış koduyla ilgileniyorum foo
. Buna ulaşmanın bir yolu var mı?
Yanıtlar:
Kullanıyorsanız , boru hattının her bir elemanının çıkış durumunu almak için dizi değişkenini bash
kullanabilirsiniz PIPESTATUS
.
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
Kullanıyorsanız zsh
, dizi çağrılır pipestatus
(durum önemlidir!) Ve dizi endeksleri birinden başlar:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
Onları bir fonksiyon 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 çalıştırın bash
veya zsh
aynı sonuçları elde edin; sadece bir tanesi retval_bash
ve retval_zsh
belirlenecek. Diğeri boş olacak. Bu, bir işlevin bitmesini sağlar return $retval_bash $retval_zsh
(tırnak işaretlerinin olmamasına dikkat edin!).
pipestatus
zsh olarak. Ne yazık ki diğer mermilerde bu özellik yoktur.
echo "$pipestatus[1]" "$pipestatus[2]"
.
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
Bunu yapmanın 3 yaygın yolu vardır:
İlk yol pipefail
seçeneği ( ksh
, zsh
veya bash
) ayarlamaktır. Bu en basittir ve yaptığı temel olarak çıkış durumunu $?
sıfır olmayan bir şekilde çıkmak için son programın çıkış koduna ayarlar (ya da hepsi başarılı bir şekilde çıkarsa sıfır).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash ayrıca, son boru hattındaki tüm programların çıkış durumunu içeren $PIPESTATUS
( $pipestatus
in zsh
) adında bir dizi değişkenine sahiptir .
$ 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 boru hattındaki belirli bir değeri almak için 3. komut örneğini kullanabilirsiniz.
Bu, çözümlerin en hantal halidir. 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
ksh
, ancak kısa bir bakışta, manpage sayfasına, desteklemiyor $PIPESTATUS
ya da benzeri bir şey yok. Yine de pipefail
seçeneği destekliyor .
LOG=$(failed_command | successful_command)
Bu çözüm, bash belirli özellikleri veya geçici dosyaları kullanmadan çalışır. Bonus: sonunda çıkış durumu aslında bir çıkış durumu olup dosyadaki bir dize değildir.
Durum:
someprog | filter
çıkış durumunu someprog
ve çıktısını almak istiyorsunuz filter
.
İş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 stdoutundan filter
ve çıkış durumundan yapının çıkış durumu olarak stdout'tur someprog
.
Bu yapı aynı zamanda {...}
alt kabuklar yerine basit komut gruplama ile çalışır (...)
. alt kabukların, burada ihtiyaç duymadığımız bir performans maliyeti olan bazı etkileri vardır. Daha fazla bilgi 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
Maalesef, bash dilbilgisi, küme parantezleri için boşluk ve noktalı virgül gerektirir, böylece yapı çok daha geniş hale gelir.
Bu metnin geri kalanında 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 miras alır. Bu, someprog
açık dosya tanımlayıcı 3 ve 4'ü devralacak demektir. Dosya tanımlayıcı 3'e someprog
yazarsa çıkış durumu olur. Gerçek çıkış durumu göz ardı edilecektir, çünkü read
sadece bir kez okur.
someprog
Dosya tanımlayıcı 3 veya 4'e yazabildiğinizden endişeleniyorsanız, aramadan önce dosya tanımlayıcılarını kapatmak en iyisidir someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
Daha exec 3>&- 4>&-
önce someprog
çalıştırmadan önce dosya tanımlayıcısını kapatır, bu someprog
yüzden someprog
bu dosya tanımlayıcıları yoktur.
Aynı zamanda şöyle 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:
#part3
) ve sağdaki ( #part2
) komutlar uygulanır. exit $xs
Aynı zamanda, borunun son komutudur ve bu, stdin'den gelen dizenin tüm yapının çıkış durumu olacağı anlamına gelir.#part2
ve tüm yapının çıkış durumu olacağı anlamına gelir.#part5
ve #part6
) ve sağdaki ( filter >&4
) komutlar uygulanır. Çıktısı filter
olarak tanımlayıcı 4. dosyaya yönlendirilir #part1
dosya tanımlayıcı 4 Stdout'a yönlendirildi. Bu, çıktının filter
tüm yapının stdout olduğu anlamına gelir .#part6
dosya tanıtıcısına 3 yazdırılır. #part3
Dosya tanıtıcısında 3 yönlendirildi #part2
. Bu, çıkış durumunun #part6
tüm yapının son çıkış durumu olacağı anlamına gelir .someprog
Idam edildi. Çıkış durumu alınır #part5
. Stdout, boru tarafından alınır #part4
ve iletilir filter
. filter
İrade çıktısı sırayla açıklandığı gibi stdout'a ulaşacaktır.#part4
(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)
basitleştirir someprog 3>&- 4>&-
.
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
Tam olarak istediğin gibi olmasa da,
#!/bin/bash -o pipefail
Böylece borularınız son sıfır olmayan dönüşü döndürür.
biraz daha az kodlama olabilir
Düzenleme: Örnek
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefail
betiğin içinde daha sağlam olması gerekir, örneğin birinin komut dosyasını çalıştırması durumunda bash foo.sh
.
-o pipefail
POSIX’de olmadığını unutmayın .
#!/bin/bash -o pipefail
. Hata:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!
ilki dışındaki satırlardaki boşlukları ayrıştırmaz ve bu nedenle argüman olarak bir sonraki öğeyi kullanan /bin/bash
-o pipefail
/tmp/ff
gerekli /bin/bash
-o
pipefail
/tmp/ff
- getopt
(veya benzeri) ayrıştırma yerine bu olur için , böylece başarısız olur. Eğer bir sarmalayıcı yapacaksanız (şunu söylemiştiniz, sadece bunu yaptınız ve hatta sıraya koyun , bu işe yarayacaktı.) Ayrıca bakınız: en.wikipedia.org/wiki/Shebang_%28Unix%29optarg
ARGV
-o
bash-pf
exec /bin/bash -o pipefail "$@"
#!
Mümkün olduğunda ne yapacağımı çıkış kodunu foo
içine beslemektir bar
. Örneğin, foo
hiçbir zaman yalnızca basamaklı bir çizgi oluşturmadığını biliyorsanız, o zaman çıkış kodunu sabitleyebilirim:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
Veya çıktının foo
hiçbir zaman tam olarak bir çizgi içermediğini biliyorsam .
:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
Bu bar
, son satır dışındaki herkes üzerinde çalışmanın ve son satırı çıkış kodu olarak geçirmenin bir yolu varsa, her zaman yapılabilir .
Eğer bar
kimin çıkış İhtiyacınız olmayan karmaşık bir boru hattı, farklı bir dosya tanımlayıcı üzerinde çıkış kodu yazdırarak bir kısmını atlayabilir.
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
Bundan sonra $exit_codes
genellikle foo:X bar:Y
, ama olabilir bar:Y foo:X
eğer bar
onun bütün girdilere okumadan önce ya da şanssız iseniz kapanıyor. 512 bayta kadar olan borulara yazılan tüm birliklerde atomik olduğunu düşünüyorum, bu nedenle foo:$?
ve bar:$?
etiket dizeleri 507 baytın altında olduğu sürece parçalar birleştirilmeyecek.
Çıktısını yakalamanız gerekirse bar
, zorlaşır. Yukarıdaki teknikleri, bar
hiçbir zaman bir çıkış kodu göstergesine benzeyen bir çizgi içermemesi için çıktısını düzenleyerek birleştirebilirsiniz .
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 tabii ki, durumu saklamak için geçici bir dosya kullanmak için basit bir seçenek var . Basit, ama üretimde bu kadar basit değil :
/tmp
bir komut dosyasının dosya yazabildiğinden emin olduğunuz tek yer burasıdır. Kullanım mktemp
günümüzde tüm ciddi Unix'lerde POSIX ama mevcut değildir.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
İşte sadece POSIX kabuğunu kullanan ve geçici dosyalar içermeyen 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
Her durumun hangi komutu yattığını söyleyecek indekslerle, başarısız olan işlemlerin durum kodlarını rasgele sırayla 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
Testlerimdeki alıntıları not edin ; onlar olmadan grep
farklılaşamazlar çünkü yeni satırlar boşluklara zorlanır.
Bu yüzden lesmana'nınki gibi bir cevaba katkıda bulunmak istedim, ama benimkinin belki de daha basit ve biraz daha avantajlı bir saf Bourne kabuklu çö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.
Bunun en iyisi içten açıklanabileceğini düşünüyorum - komut1 normal çıktısını stdout'ta (dosya tanımlayıcı 1) yürütecek ve yazdıracak, sonra bir kez yapılırsa, printf komutun stdout'unda komut kodunu çalıştıracak ve yazacaktır, ancak bu stdout yönlendirildi dosya tanıtıcısı 3.
Komut1 çalışırken, stdout komutu2'ye yöneltiliyor (printf'un çıkışı asla komut2'ye gelmiyor, çünkü onu 1'in yerine dosya tanımlayıcısına 3 (göndeririz). Daha sonra komut2'nin çıktısını dosya tanımlayıcı 4'e yeniden yönlendiririz, böylece dosya tanımlayıcı 1'in dışında kalır - çünkü dosya tanımlayıcı 1'in biraz daha 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 ikame işlemi (backticks) yakalanacak ve değişkene yerleştirilecek olan şey budur.
Son sihir, önce exec 4>&1
ayrı bir komut olarak yaptığımız şeydir - dış tanımlayıcı stdout'unun bir kopyası olarak dosya tanımlayıcı 4'ü açar. Komut yerine koyma, standartlar üzerine yazılmış komutları, içindeki komutlar perspektifinden ele geçirir - ancak komut2'nin çıktısı, komut yerine koyma söz konusu olduğunda dosya tanımlayıcısına (4) gidecektir, ancak komut yerine koyma, onu yakalamaz. Komut ikame işleminden "çıktıktan" sonra, betiğin genel dosya tanımlayıcısı 1'e etkin bir şekilde devam ediyor.
( exec 4>&1
Ayrı bir komut olması gerekir, çünkü birçok genel kabuk, bir komut değiştirmenin içindeki bir dosya tanımlayıcısına yazmayı denediğinizde, değiştirmeyi kullanan "harici" komutunda açılmasından hoşlanmaz. Bunu yapmanın en basit yolu.
Komutların çıktıları birbirinden sıçramış gibi: Daha az teknik ve daha eğlenceli bir şekilde bakabilirsiniz: command1 boruları command2'ye, sonra printf çıkışı komut 2'nin üzerine atlar, böylece komut2 onu yakalamaz ve sonra komut 2'nin çıktısı aynen ikame tarafından algılanmak üzere tam zamanında çıktıkça değişken iktidarında sona erecek ve komut2'nin çıktısı standart çıktısına yazıldığı gibi neşeli bir şekilde devam eder. normal bir boruda.
Ayrıca, anladığım kadarıyla, $?
hala borudaki ikinci komutun dönüş kodunu içerecektir, çünkü değişken atamalar, komut değişimleri ve bileşik komutların hepsi içlerindeki komutun dönüş koduna etkili bir şekilde şeffaftır, bu nedenle dönüş durumu komut2'nin yayılması gerekir - bu ve ek bir işlev tanımlamak zorunda kalmamak, bunun lesmana tarafından önerilenden biraz daha iyi bir çözüm olabileceğini düşünüyorum.
Uyarılar lesmana'nın bahsettiği gibi, komut 1'in bir noktada 3 veya 4 dosya tanımlayıcıları kullanarak bitmesi muhtemeldir, bu nedenle daha sağlam olması için şunları yapmalısınız:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Örneğimde bileşik komutlar kullandığımı unutmayın, ancak alt kabuklar ( ( )
bunun yerine kullanmak { }
da işe yarayacaktır, ancak daha az verimli olabilirler.)
Komutlar, dosya tanımlayıcılarını, onları başlatan işlemden devralır, böylece ikinci satırın tamamı dosya tanımlayıcı dört'ü miras alır ve ardından izlenen bileşik komut 3>&1
dosya tanımlayıcı 3'ü miras alır. Böylece, 4>&-
iç bileşik komutunun 3>&-
dosya tanımlayıcı dört'ü miras almayacağından ve dosya tanımlayıcı üçünü miras almayacağından emin olunur, böylece komut1 daha temiz, daha standart bir ortam elde eder. Ayrıca içini 4>&-
yanına da taşıyabilirsiniz 3>&-
, ancak neden kapsamını mümkün olduğu kadar kısıtlamadığını anlıyorum.
Dosyaların üç ve dördüncü dosya tanımlayıcılarını ne sıklıkta kullandıklarından emin değilim - bence çoğu zaman kullanılmayan dosya tanımlayıcılarını döndüren programların çoğu zaman sistem tanımlayıcılarını döndüren sistemler kullanır, ancak bazen kod tanımlayıcı 3'e doğrudan dosya yazar. tahmin (açık olup olmadığını görmek için bir dosya tanımlayıcısını denetleyen ve açıksa kullanıp kullanmadığını veya açık değilse farklı davranan bir program hayal edebiliyorum). Bu nedenle, ikincisi genel amaçlı durumlar için akılda tutulması ve kullanılması muhtemeldir.
-bash: 3: Bad file descriptor
.
Eğer varsa moreutils paketi yüklü kullanabilirsiniz mispipe İstediğin tam olarak ne yapar hangi programı.
lesmana'nın yukarıdaki çözümü, { .. }
bunun yerine iç içe geçmiş alt işlemlerin ek yükü olmadan da yapılabilir (bu gruplandırılmış komutların her zaman noktalı virgülle bitmesi gerektiğini unutmayın). Bunun gibi bir şey:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
Ben bu yapı çizgi sürüm 0.5.5 ve bash sürüm 3.2.25 ve 4.2.42 ile kontrol ettim, bu nedenle bazı mermiler { .. }
gruplandırmayı desteklemese bile, hala POSIX uyumludur.
set -o pipefail
, ksh veya herhangi bir sayıdaki serpilmiş wait
komutla bile çalışmasını sağlayamıyorum . Sanırım en azından, ksh için ayrıştırma sorunu olabilir, sanki alt kabukları kullanmaya devam edersem daha sonra düzgün çalışır, ancak bir if
ksh için alt kabı türevini seçmekle birlikte, ancak diğerleri için bileşik komutları bırakarak başarısız olur .
Bu taşınabilirdir, yani herhangi bir POSIX uyumlu kabukla çalışır, geçerli dizinin yazılabilir olmasını gerektirmez ve aynı hile kullanan birden çok komut dosyasının aynı anda çalışmasına izin verir.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Düzenleme: Burada Gilles '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 değişken:
(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: Bash ile daha kolay olduğuna katılıyorum, ancak bazı durumlarda Bash'in nasıl önleneceğini bilmek de buna değer.
Yaygın çözümlerden birini kullanamamanız durumunda, @Patrik'in cevabına bir eklenti olarak atıfta bulunulmaktadır.
Bu cevap aşağıdakileri varsaymaktadır:
$PIPESTATUS
ne deset -o pipefail
Ek varsayımlar. Hepsinden kurtulabilirsiniz, ancak bu tarifleri çok fazla püskürtür, bu yüzden burada ele alınmaz:
- Bilmek istediğiniz tek şey PIPE'deki tüm komutların 0 çıkış koduna sahip olmasıdır.
- Ek yan bant bilgisine ihtiyacınız yoktur.
- Kabuğunuz tüm boru komutlarının geri dönmesini bekler.
Önce: foo | bar | baz
Ancak bu yalnızca son komutun ( baz
) çıkış kodunu döndürür.
Aranıyor: borudaki komutlardan herhangi biri başarısız olursa, (doğru) $?
olmamalıdır0
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
. Bu genellikle hemen bir dosya oluşturur./tmp
wait
için gerekli ksh
, çünkü ksh
başka tüm komutların bitmesini beklemiyor. Ancak, bazı arka plan görevleri varsa istenmeyen yan etkilerin olduğunu lütfen unutmayın, bu nedenle varsayılan olarak yorumladım. Bekleme zarar vermezse, yorum yapabilirsiniz.read
döner false
, bu nedenle true
bir hata olduğunu belirtirBu tek bir komut için eklenti yerine kullanılabilir ve sadece aşağıdakileri gerektirir:
/proc/fd/N
Bug:
Bu komut dosyasında /tmp
boş alan kalmazsa bir hata var . Bu yapay duruma karşı da korunmaya ihtiyacınız varsa, bunu aşağıdaki gibi yapabilirsiniz, ancak bunun dezavantajı vardır; bu, giriş sayısının , borudaki komut sayısına bağlı 0
olmasına 000
bağlı olarak biraz daha karmaşıktır:
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 sadece son boru komut bekle benzer kabuklar ihtiyaç wait
uncommented
Son örnek printf "%1s" "$?"
bunun yerine echo -n "$?"
daha taşınabilir olduğundan bunun yerine kullanır . Her platform -n
doğru yorum yapmıyor.
printf "$?"
aynı zamanda printf "%1s"
senaryoyu gerçekten kırılmış bir platformda çalıştırmanız durumunda bazı köşe davalarını da yakalar. (Okuyun: programlamaya devam ederseniz paranoia_mode=extreme
)
FD 8 ve FD 9, birden çok basamağı destekleyen platformlarda daha yüksek olabilir. AFAIR POSIX uyumlu bir kabuğun yalnızca tek basamakları desteklemesi gerekir.
Debian 8.2 ile test edildi sh
, bash
, ksh
, ash
, sash
ve hattacsh
Biraz önlem ile, bu çalışması gerekir:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
Aşağıdaki 'if' bloğu yalnızca 'komut' başarılı olduğunda çalışır:
if command; then
# ...
fi
Özellikle konuşursak, şöyle bir şey çalıştırabilirsin:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
Hangi haconf -makerw
stdout ve stderr 'i "$ haconf_out"' a koyacak ve saklayacaktır. Döndürülen değer haconf
true ise, o zaman 'if' bloğu çalıştırılacak ve grep
"$ haconf_out" u okuyacak, "Küme zaten yazılabilir" ile eşleştirmeye çalışacaktır.
Borular otomatik olarak kendilerini temizler dikkat edin; yönlendirme ile "$ haconf_out" işlemi bittiğinde kaldırmak için dikkatli olmalısınız.
pipefail
Bu işlevsellik elinizin altında değilse, o kadar zarif değil , ama meşru bir alternatif.
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
$
(En azından bash ile) set -e
bir taneyle açık bir şekilde boru arızasını taklit etmek ve boru hatasından çıkmak için alt kabuk kullanabilir
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
Bu foo
nedenle, bir nedenden ötürü başarısız olursa - programın geri kalanı yürütülmez ve komut dosyası ilgili hata koduyla çıkar. (Bu foo
, hatanın nedenini anlamak için yeterli olan kendi hatasını yazdırdığını varsayar )
EDIT : Bu cevap yanlış, ama ilginç, bu yüzden ileride başvurmak için bırakacağım.
!
Komuta
a eklemek dönüş kodunu tersine çevirir.
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
tersine çevirmek istemiyorumbogus_command