İşlem Çıkış Koduna Göre Çıkış Kabuğu Komut Dosyası


378

Birkaç komut yürüten bir kabuk komut dosyası var. Komutlardan herhangi biri sıfır dışında bir çıkış koduyla çıkarsa kabuk komut dosyasının çıkışını nasıl yapabilirim?


3
Bash kullandığınızı varsayarak yanıtladım, ancak çok farklı bir kabuksa gönderinizde belirtebilirsiniz?
Martin W

Zor yöntem: $?her komuttan sonra değerini test edin . Kolay yöntem: Bash betiğinizin üstüne set -eveya #!/bin/bash -eüstüne koyun .
mwfearnley

Yanıtlar:


494

Her komuttan sonra, çıkış kodu $?değişkende bulunabilir, böylece şöyle bir şey elde edersiniz:

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

Borulu komutlara dikkat etmeniz gerekir, çünkü $?sadece size borudaki son öğenin dönüş kodunu verir, böylece kodda:

ls -al file.ext | sed 's/^/xx: /"

dosya yoksa bir hata kodu döndürmez ( sedboru hattının bir kısmı gerçekten çalıştığından 0 döndürür).

bashKabuk, aslında, bu durumda yardımcı olacak bir dizi sağlar olmak o PIPESTATUS. Bu dizi, her bir boru hattı bileşeni için ayrı ayrı erişebileceğiniz bir öğeye sahiptir ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]}
1

Bunun falsekomut satırının tamamını değil, komutun sonucunu aldığını unutmayın . Ayrıca, listenin tamamını uygun gördüğünüz şekilde işleyebilirsiniz:

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

Bir boru hattından en büyük hata kodunu almak istiyorsanız, aşağıdaki gibi bir şey kullanabilirsiniz:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

Bu, her bir PIPESTATUSöğeden sırayla geçer rcve önceki rcdeğerden büyükse depolar.


39
Tek bir taşınabilir kod satırında aynı özellik: ls -al file.ext || exit $?([[]] taşınabilir değil)
MarcH

19
MarcH, bence bu [[ ]]oldukça portatif bash, bu soru etiketli budur :-) Gariptir, lsişe yaramaz, command.combu yüzden de taşınabilir değil, biliyorum, ama aynı tür bir argüman mevcut.
paxdiablo

39
Bunun eski olduğunu biliyorum, ancak komut aracılığıyla komutların çıkış kodunu dizi aracılığıyla alabileceğinizi belirtmeliyiz PIPESTATUS(yani, ${PIPESTATUS[0]}ilk komut ${PIPESTATUS[1]}için, ikinci ${PIPESTATUS[*]}için veya tüm çıkış durumlarının bir listesi için
DevSolar

11
Zarif ve deyimsel kabuk komut dosyalarının çok nadiren $?doğrudan incelenmesi gerektiği vurgulanmalıdır . Genellikle if ls -al file.ext; then : nothing; else exit $?; fi@MarcH'ın dediği gibi bir şey istiyorsunuz , ls -al file.ext || exit $?ancak thenveya elsecümleleri biraz daha karmaşıksa, daha sürdürülebilir.
tripleee

9
[[ $rc != 0 ]]size bir verecek 0: not foundveya 1: not foundhata. Bu olarak değiştirilmelidir [ $rc -ne 0 ]. Ayrıca rc=$?daha sonra çıkarılabilir ve sadece kullanılabilir [ $? -ne 0 ].
CurtisLeeBolin

223

$? İle çalışmak istiyorsanız, $? 'Dan beri her komuttan sonra kontrol etmeniz gerekir. her komut çıktıktan sonra güncellenir. Bu, bir boru hattı yürütürseniz, yalnızca boru hattındaki son işlemin çıkış kodunu alacağınız anlamına gelir.

Başka bir yaklaşım bunu yapmaktır:

set -e
set -o pipefail

Bunu kabuk betiğinin en üstüne koyarsanız, bash bununla ilgilenecek gibi görünüyor. Bir önceki posterin belirttiği gibi, "set -e" bash'ın herhangi bir basit komutta bir hata ile çıkmasına neden olur. "set -o pipefail", bash'ın bir boru hattındaki herhangi bir komutta hata ile çıkmasına neden olur.

Bu sorun hakkında biraz daha fazla tartışma için buraya veya buraya bakın . İşte seti yerleşiğini üzerinde bash manuel bölümdür.


6
Bu en iyi cevap olmalı: Bunu yapmak, PIPESTATUSçıkış kodlarını her yerde kullanmak ve kontrol etmekten çok, çok daha kolay .
candu

2
#!/bin/bash -ekabuk betiğini başlatmanın tek yoludur. foo || handle_error $?Çıkış durumlarını gerçekten incelemeniz gerekiyorsa her zaman kullanabilirsiniz .
Davis Herring

53

" set -e" muhtemelen bunu yapmanın en kolay yoludur. Bunu programınızdaki herhangi bir komuttan önce koymanız yeterlidir.


6
@SwaroopCH set -e, komut dosyanızdaki herhangi bir komut hata durumuyla çıkarsa ve bu hatayı işlemediğiniz takdirde komut dosyanızı iptal eder.
Andrew

2
set -eset -o errexitöncekinden farklı olarak % 100 eşdeğerdir . Resmi belgeler için opengroup + errexit arayın.
MarcH

30

Bash'da exit'i parametre olmadan çağırırsanız, son komutun çıkış kodunu döndürür. VEYA ile birlikte bash yalnızca önceki komut başarısız olursa exit komutunu çağırmalıdır. Ama bunu test etmedim.

komut1 || çıkış;
komut2 || çıkış;

Bash ayrıca son komutun çıkış kodunu $? Değişkeninde saklar.


25
[ $? -eq 0 ] || exit $?; # exit for none-zero return code

3
Bu olmamalı exit $?(ebeveyn yok)?
Malthe

21

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. Nasıl çıkış kodunu alabilirim cmd1içindecmd1|cmd2

    İlk olarak, cmd1çıkış kodunun sıfırdan farklı olabileceğini ve yine de bir hata anlamına gelmediğini unutmayın. Bu, örneğin

    cmd | head -1

    141 (veya ksh93 ile 269) çıkış durumunu gözlemleyebilirsiniz cmd1, ancak bunun nedeni bir satırı okuduktan sonra sonlandırıldığında cmdbir SIGPIPE sinyali tarafından kesilmesidir head -1.

    Bir boru hattı elemanlarının çıkış durumunu bilmek cmd1 | cmd2 | cmd3

    a. zsh ile:

    Çıkış kodları pipetatus özel dizisinde sağlanır. cmd1çıkış kodu girilir $pipestatus[1], cmd3çıkış kodu girilir $pipestatus[3], böylece $?her zaman aynı olur $pipestatus[-1].

    b. bash ile:

    Çıkış kodları PIPESTATUSözel dizide sağlanır. cmd1çıkış kodu girilir ${PIPESTATUS[0]}, cmd3çıkış kodu girilir ${PIPESTATUS[2]}, böylece $?her zaman aynı olur ${PIPESTATUS: -1}.

    ...

    Daha fazla ayrıntı için aşağıdaki bağlantıya bakın .


19

bash için:

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}

19
Muhtemelen "çıkış 0" olmamalıdır, çünkü bu başarıyı gösterir.
nobar

3
exit_code = $ ?; echo "hata nedeniyle komut dosyası iptal edildi"; exit $ exit_code
RaSergiy

1
@ HAL9001 Bunun kanıtı var mı? IBM belgeleri aksini söylüyor .
Patrick James McDougle

11

Bash'da bu kolay, sadece && ile bağla:

command1 && command2 && command3

Nested if yapısını da kullanabilirsiniz:

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi

+1 Bu aradığım en basit çözümdü. Ayrıca if (! command), komuttan sıfır dışında bir hata kodu bekliyorsanız da yazabilirsiniz .
Berci

Bu ardışık komutlar içindir .. eğer bu 3'ü paralel olarak başlatmak ve bunlardan herhangi biri başarısız olursa herkesi öldürmek istersem?
Vasile Surdu

4
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit

2
Kullanmak için nadiren bir neden vardır $*; "$@"boşlukları ve joker karakterleri korumak için kullanın .
Davis Herring
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.