Bir komut başarısız olursa nasıl çıkılır?


222

Ben kabuk komut dosyasında bir çaylakım. Bir komut yazdırılamazsa bir ileti yazdırmak ve komut dosyamdan çıkmak istiyorum. Denedim:

my_command && (echo 'my_command failed; exit)

ama bu çalışmıyor. Komut dosyasında bu satırı izleyen talimatları yürütmeye devam eder. Ubuntu ve bash kullanıyorum.


5
Kapatılmamış alıntıyı başarısız / çıkışa neden olacak bir sözdizimi hatası olarak mı düşündünüz? değilse, örneğinizdeki teklifi kapatmalısınız.
Ocaklar

Yanıtlar:


407

Deneyin:

my_command || { echo 'my_command failed' ; exit 1; }

Dört değişiklik:

  • Değişim &&için||
  • Kullanım { }yerine( )
  • ;Sonra tanıtmak exitve
  • sonrası {ve sonrası boşluklar}

İletiyi yazdırmak ve yalnızca komut başarısız olduğunda (sıfırdan farklı bir değerle çıkar) çıkmak istediğinizden ||bir &&.

cmd1 && cmd2

başarılı cmd2olduğunda çalışır cmd1(çıkış değeri 0). Buna karşılık

cmd1 || cmd2

çalışacaktır cmd2zaman cmd1(çıkış değeri sıfır olmayan) başarısız olur.

Kullanma ( ), içindeki komutu alt kabukta çalıştırır ve birexit oradan , orijinal kabuğunuzdan değil, alt kabuktan çıkmanıza neden olur, bu nedenle yürütme orijinal kabuğunuzda devam eder.

Bu kullanımın üstesinden gelmek için { }

Son iki değişiklik bash tarafından gereklidir.


4
"Tersine çevrilmiş" gibi görünüyor. Bir işlev "başarılı" olursa 0 döndürür ve "başarısız olursa" sıfırdan farklı döndürür, bu nedenle && ilk yarıyı sıfırdan farklı döndürdüğünde değerlendirmesi beklenebilir. Bu, yukarıdaki cevabın yanlış olduğu anlamına gelmez - hayır doğru. && ve || komut dosyalarında, dönüş değerine değil, başarıya dayalı olarak çalışır.
CashCow

7
Ters görünüyor, ama okuyun ve mantıklı: "Bu komutu (başarılı bir şekilde) yapın" VEYA "bu hatayı yazdırın ve çıkın"
simpleuser

2
Arkasındaki mantık, dilin kısa devre değerlendirmesi (SCE) kullanmasıdır. SCE ile, f ifadesi "p OR q" biçimindedir ve p doğru olarak değerlendirilir, o zaman q'ya bakmak için bile bir neden yoktur. İfade "p VE q" biçimindeyse ve p yanlış olarak değerlendirilirse, q'ya bakmak için bir neden yoktur. Bunun nedeni iki kattır: 1) daha hızlı, bariz nedenlerle ve 2) belirli hata türlerinden kaçınır (örneğin: "eğer x! = 0 VE 10 / x> 5", SCE yoksa çökecektir ). Komut satırında bu şekilde kullanma yeteneği mutlu bir yan etkidir.
user2635263

2
{ (>&2 echo 'my_command failed') ; exit 1; }
STDERR'e

127

Diğer cevaplar doğrudan soruyu iyi ele aldı, ancak kullanmak da ilginizi çekebilir set -e. Bununla, başarısız olan herhangi bir komut ( iftestler gibi belirli bağlamların dışında ) komut dosyasının iptal edilmesine neden olur. Belirli komut dosyaları için çok kullanışlıdır.


3
Ne yazık ki, aynı zamanda çok tehlikelidir - set -ene zaman çalıştığı ve çalışmadığı zaman uzun ve gizli bir kurallar dizisine sahiptir. (Eğer birisi çalışırsa if yourfunction; then ..., o set -ezaman asla içeride yourfunctionveya çağırdığı herhangi bir şeyde ateş etmez, çünkü bu kod "işaretli" kabul edilir) Bunu ve diğer tuzakları tartışan BashFAQ # 105'in egzersizler bölümüne bakın (bazıları sadece belirli kabuk sürümleri için geçerlidir, bu nedenle komut dosyanızı çalıştırmak için kullanılabilecek her sürüme karşı test ettiğinizden emin olun).
Charles Duffy

67

Ayrıca, her komutun çıkış durumunun, komutu çalıştırdıktan hemen sonra kontrol edebileceğiniz kabuk değişkeni $? Sıfırdan farklı bir durum hatayı gösterir:

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi

10
Bu sadece my_command ise değiştirilebilir, burada test komutunu kullanmaya gerek yoktur.
Bart Sas

5
+1 çünkü bu alternatifi listelemek için cezalandırılmamanız gerektiğini düşünüyorum - masada olmalı - biraz çirkin ve deneyimlerime göre testin doğasını fark etmeden aralarında başka bir komut çalıştırmak çok kolay (belki ben ' Ben sadece aptalım).
Tony Delroy

1
@BartSas Komut uzunsa, kendi satırına koymak daha iyidir. Komut dosyasını daha okunaklı hale getirir.
Michael

@Michael, B hattını anlayabilmeniz için A hattında neler olduğunu bilmeniz gereken (veya daha da kötüsü, bir şey eklemenin güvenli olduğunu bilmeden önce B satırında neler olacağını bilmeniz gereken satırlar arasında bağımlılıklar oluşturuyorsa değil) A çizgisinin sonundan sonra yeni). Birisinin bir daha eklediğini gördüm echo "Just finished step A", bunun echoüzerine yazıldığını bilmeden $?daha sonra kontrol edilecekti.
Charles Duffy

67

Bu davranışı betiğinizdeki tüm komutlar için istiyorsanız,

  set -e 
  set -o pipefail

betiğin başında. Bu seçenek çifti, bash yorumlayıcısına bir komut sıfırdan farklı bir çıkış koduyla döndüğünde çıkmasını söyler.

Bu bir çıkış mesajı yazdırmanıza izin vermez.


8
Çıkışta komutları trapbash yerleşik komutunu kullanarak çalıştırabilirsiniz .
Gavin Smith

Bu XY sorununun cevabı.
jwg

5
Çifti yerine bağımsız olarak hangi komutların ne yaptığını açıklamak ilginç olabilir. Özellikle set -o pipefailde istenen davranış olmayabilir. Yine de işaret ettiğin için teşekkürler!
Antoine Pinsard

3
set -ehiç güvenilir, tutarlı veya öngörülebilir değildir! Kendinizi buna ikna etmek için, BashFAQ # 105'in egzersizler bölümünü veya in-ulm.de/~mascheck/various/set-e
Charles Duffy

14

Aşağıdaki deyimi hackledim:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

Her komuttan önce bilgilendirici bir yankı yazın ve her komutu aynı
if [ $? -ne 0 ];...satırla izleyin . (Elbette, isterseniz bu hata mesajını düzenleyebilirsiniz.)


Bu bir işlev olarak tanımlanabilir, böylece yeniden kullanılabilir.
Eric Wang

2
'[$? -ne 0]; o zaman ... fi 'yazmak için karmaşık bir yoldur' || '
kevin cline

if ! javac *.java; then ...- neden hiç uğraşmıyorsun $??
Charles Duffy

Charles - en iyi ihtimalle vasat bir bash betikiyim :)
Grant Birchmeier

10

Sağlanan my_commandkanonik olarak tasarlanmıştır, yani başarılı olduğunda 0 döndürür, o &&zaman istediğiniz şeyin tam tersidir. Sen istiyorsun ||.

Ayrıca (bash bana doğru görünmüyor unutmayın , ama nerede olduğumu deneyemiyorum. Bana söyle.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}

IIRC, {}
Alex Howansky

9
@Alex: ayrı bir çizgideler.
sonraki duyuruya kadar duraklatıldı.

5

Ayrıca, çıkış hatası durumunu korumak ve her satıra bir komut içeren okunabilir bir dosyaya sahip olmak için kullanabilirsiniz:

my_command1 || exit $?
my_command2 || exit $?

Ancak bu ek hata mesajı yazdırmaz. Ancak bazı durumlarda, hata yine de başarısız komut tarafından yazdırılır.


1
Bunun için bir sebep yok exit $?; sadece varsayılan değeri olarak exitkullanır $?.
Charles Duffy

@CharlesDuffy bilmiyordu. Harika, bu yüzden daha da basit!
alexpirine

3

trapKabuk yerleşik (yani, sıfır olmayan bir dönüş durumu) başarısız oldu komut yürütme dahil olmak üzere alıcı sinyalleri ve diğer yararlı koşulları sağlar. Bu nedenle, her komutun dönüş durumunu açıkça test etmek istemiyorsanız trap "your shell code" ERRve komut sıfır olmayan bir durum döndürdüğünde kabuk kodu yürütülür. Örneğin:

trap "echo script failed; exit 1" ERR

Diğer başarısız komutları yakalama durumlarında olduğu gibi, boru hatlarının özel bir işleme ihtiyacı olduğunu unutmayın; yukarıdakiler yakalanmaz false | true.

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.