Bir komut başarılı veya başarısız olursa şartlı olarak bir şey nasıl yapılır


283

Bash böyle bir şey nasıl yapabilirim?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

Yanıtlar:


359

Bir komut başarılı veya başarısız olursa şartlı olarak bir şey nasıl yapılır

Tam olarak bash'ın ififadesi şöyle:

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Yorumlardan bilgi ekleme: Bu durumda ... sözdizimini kullanmanıza gerek yoktur . kendisi ise neredeyse eşdeğer bir komuttur . Muhtemelen en yaygın kullanılan komuttur ve bu, kabuğun sözdiziminin bir parçası olduğu varsayımına yol açabilir. Ancak bir komutun başarılı olup olmadığını test etmek istiyorsanız , yukarıda gösterildiği gibi komutu doğrudan kullanın .[][testifif

Düzenleme: Açıklık için soruyu en üste yazdı (bu cevap sayfanın en üstünde görünmüyor).


11
Noktalı virgülün önemli olduğunu unutmayın.
Thorbjørn Ravn Andersen 17:11

21
Ya da sadece thenayrı bir çizgi koyabilirsiniz .
l0b0

36
@Joe: Sanırım demek istiyorsun if ! command ; then ... ; fi. [kendisi bir emirdir ve bu durumda gerekli değildir.
Keith Thompson

20
@Joe: Benim yolum da doğru olma erdemine sahiptir. if [ ! command ]yürütmez command; commandbir dize gibi davranır ve onu sıfır gibi olmayan bir uzunluğa sahip olduğu için doğru olarak değerlendirir. komutanın [eş anlamlısıtest
Keith Thompson

5
Hata. Haklısın
Joe

133

Bir kabuk komutu çalışıyorsa, olmasını istediğiniz küçük şeyler için &&yapıyı kullanabilirsiniz :

rm -rf somedir && trace_output "Removed the directory"

Benzer şekilde, bir kabuk komutu başarısız olduğunda gerçekleşmesini istediğiniz küçük şeyler için şunları kullanabilirsiniz ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

Ya da her ikisi de

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Muhtemelen bu yapılarda çok fazla şey yapmak pek mümkün değildir, ancak zaman zaman kontrolün akışını daha net hale getirebilirler.


3
Daha kısa ve (en azından bazı mermilerde) daha hızlı. Bir keresinde deşifre etmeye çalıştığım bu şartlı yapılarla yazılmış bir canavar Ultrix kurulum senaryosunu hatırlamaya titriyorum ...
vonbrand

101

$?En son komutun / fonksiyonun yürütülmesinin sonucunu içeren değerini kontrol edin :

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
Teknik olarak doğru olsa da (ve bu nedenle bir aşağı oy kullanmazsanız), Bash'in ifdeyimini kullanmıyor . Keith Thompson'un cevabını tercih ederim.
janmoesen

16
Bu deyimin yararları vardır - dönüş değerini korur. Sonuçta, bunu daha güçlü, daha ayrıntılı olsa da buluyorum. Ayrıca okumak daha kolaydır.
taksiler 7:15

2
"Bash'in deyimi varsa" nedir?
Nowaker,

3
@Şimdilik Tek amacının ifbunu yapmak olduğu gerçeği . Bash'deki akış kontrol koşullarının tümü $?perde arkasını inceler ; Onlar böyle yapar. Değerinin açıkça incelenmesi, vakaların çoğunda gereksiz olmak zorundadır ve genellikle acemi bir antipattern'dir.
üçlü

1
@lunakid Çıkış kodunu umursamıyorsanız if ! cmd, sorun değil. Aksi takdirde, ortak bir düzenleme bir elsefıkra kullanmaktır . Boş bırakamazsınız thenama bazen no-op'un then : nothing; elsenerede :önemli olduğunu görürsünüz . trueorada da çalışacaktı.
üçlü

47

Bu benim için çalıştı:

command && echo "OK" || echo "NOK"

commandbaşarılı olursa , o echo "OK"zaman yürütülür ve başarılı olduğundan, yürütme orada durur. Aksi takdirde &&atlanır ve echo "NOK"yürütülür.


5
Arızalanırsa bir şey yapmak ve çıkış kodunu korumak istiyorsanız (komut isteminde göstermek veya bir komut dosyasında test etmek için), şunları yapabilirsiniz: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler

2
@ Sam-Hasler: Bu olmamalı command && echo "OK" || (c=$?; echo "NOK"; (exit $c))mı?
jrw32982

9
Ayrıca, echo "OK"parça kendi kendine başarısız olursa, o zaman bu daha iyidir:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982

@ jrw32982, Nice, Eski yapıyı kullandım, ikincisini kullandım.
Sam Hasler

Asıl cevap yorumlarda, teşekkürler!
Joshua Pinter

6

Bu unutulmamalıdır if...then...five &&/ ||biz (başarısına 0) test etmek istiyorum komutu tarafından döndürülen çıkış durumu ile yaklaşım fırsatlar tipi; bununla birlikte, bazı komutlar, komut başarısız olursa veya girişle baş edemediğinde sıfır olmayan bir çıkış durumu döndürmez. Bu, olağan ifve &&/ ||yaklaşımların bu belirli komutlar için işe yaramayacağı anlamına gelir .

Örneğin, Linux'ta GNU fileargüman olarak mevcut olmayan bir dosya aldıysa ve findbelirtilen dosya kullanıcısını bulamadıysa hala 0 ile çıkar .

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

Bu gibi durumlarda, durumu ele alabilmemizin olası bir yolu , örn. Komutla döndürülenler veya komutun çıktısını ayrıştırma gibi , stderr/ stdinmesaj okumaktır . Bu amaçla, ifade kullanılabilir.filefindcase

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

Bulaşabileceğim en fazla hata şuydu:

  • İlk önce, değeri al. Diyelim ki şöyle bir şey yaptınız:

RR=$?

Şimdi, sadece bu durum için değil, karşılaşabileceğiniz diğer kişiler için, şunları göz önünde bulundurun:

tanımlanmış değişken:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Cevap: evet

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Cevap: hayır

Tanımsız değişken:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Cevap: hayır

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Cevap: evet

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Cevap: evet

Bu her türlü hata olasılığını önler.

Muhtemelen tüm sözdizimlerinin farkındasınızdır, fakat işte bazı ipuçları:

  • Tırnak kullanın. Bir kaçının "blank"olmak nothing.
  • Değişkenler için yeni modern gösterim ${variable}.
  • Numaranızdan önce birleştirilmiş bir sıfır eklemek de "hiç sayı yok" dan kaçınır.
  • Fakat bekleyin, sıfır eklemek sayının artmasını sağlar base-8. Gibi bir hata alırsınız:
    • value too great for base (error token is "08")Yukarıdaki numaralar için 7. Bu 10#oyuna girdiğinde:
    • 10#sayıyı zorlar base-10.

1

Bunu yapabilirsiniz:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

9
Bu işe yarıyor ama kıvrılıyor. Ping, bir alt kabuğun alt kabuğunda çalıştırılıyor, çıktısı pingkomut olarak çalıştırılıyor olarak görüntüleniyor. Ancak çıktı / dev / null dizinine yönlendirildiği için her zaman boş dize olacak. Yani bir alt kabukta hiçbir şey çalıştırmıyorsunuz, bu, önceki çıkış durumunun (ping komutunun alt değiştirme komutunun, yani ping) tutulacağı anlamına gelir. Açıkçası, doğru yol if ping ...; thenburada.
Stéphane Chazelas

1

Bu konuda başka bir yerde belirtildiği gibi, asıl soru temel olarak kendisine cevap verir. İşte ifkoşulların da iç içe geçebileceğini gösteren bir çizim .

Bu örnek, ifbir dosyanın olup olmadığını ve normal bir dosya olup olmadığını kontrol etmek için kullanır . Bu koşullar doğruysa, 0'dan büyük bir boyuta sahip olup olmadığını kontrol edin.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
Cevabın öyle ama cevabınız soruyu cevaplamıyor.
Jeff Schaller

@JeffSchaller, notunuz için teşekkürler. Gönderime soruyu referans olarak dahil etmek üzere düzenledim.
quartzinquartz

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
Bu, kabul edilen cevaba oldukça benziyor ; lütfen öznitelik Eğer yeniden ediyoruz işi veya cevaplar içine kendi çaba ekleyin.
Jeff Schaller

-3

Bu işlem $?, size en son yapılan komutun durumunu veren şekilde yapılabilir .

Yani olabilir

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

1
Downvote: Bu basitçe, unidiomatic olduğu için eleştirilen haklı bir önceki cevabı ifade ediyor.
üçlü,
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.