Herhangi bir komut sıfırdan farklı bir değer döndürürse kabuk komut dosyası iptal ediliyor mu?


437

Birkaç komut çağırır bir Bash kabuk komut dosyası var. Komutlardan herhangi biri sıfırdan farklı bir değer döndürürse, kabuk komut dosyasının otomatik olarak 1 dönüş değeriyle çıkmasını istiyorum.

Bu, her komutun sonucunu açıkça kontrol etmeden mümkün müdür?

Örneğin

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

8
Ayrıca set -e, set -u(veya set -eu) de yapın. -uvarolmayan herhangi bir değişkene erişebileceğiniz ve tanı koymadan üretilen boş bir değere sahip olabileceğiniz aptal, hata gizleme davranışına son verir.
Kaz

Yanıtlar:


742

Bunu betiğin başına ekleyin:

set -e

Bu, basit bir komut sıfır dışında bir çıkış değeriyle çıkarsa kabuğun hemen çıkmasına neden olur. Basit bir komut, bir if, while veya testin bir parçası olmayan veya && veya || liste.

Daha fazla bilgi için "set" dahili komutundaki bash (1) kılavuz sayfasına bakınız.

Ben şahsen "set -e" ile bütün kabuk betiklerini çalıştırıyorum. Ortada bir şey başarısız olduğunda ve komut dosyasının geri kalanı için varsayımları bozduğunda bir komut dosyasının inatla devam etmesi gerçekten sinir bozucu.


36
Bu işe yarardı, ancak "#! / Usr / bin / env bash" kullanmayı seviyorum çünkü sık sık / bin dışında bir yerden bash çalıştırıyorum. Ve "#! / Usr / bin / env bash -e" çalışmıyor. Ayrıca, hata ayıklama için izlemeyi açmak istediğinizde "set -xe" okumak için değiştirmek için bir yer olması güzel.
Ville Laurikari

48
Ayrıca, komut dosyası olarak çalıştırılırsa, shebang satırındaki bayraklar yok sayılır bash script.sh.
Tom Anderson

27
Sadece bir not: Eğer bash betiğinin içindeki işlevleri bildirirseniz, bu işlevi genişletmek istiyorsanız, işlevlerin işlev gövdesi içinde yeniden tanımlanmış olması gerekir.
Jin Kim

8
Ayrıca, senaryonuzu kaynak yaparsanız, shebang satırı ilgisiz olacaktır.

4
@JinKim Bash 3.2.48'de durum böyle görünmüyor. Bir komut dosyası içinde aşağıdaki deneyin: set -e; tf() { false; }; tf; echo 'still here'. set -eBedenin içinde olmadan bile tf(), yürütme durdurulur. Belki de bunun alt kabuklarset -e tarafından miras alınmadığını söylemek istediniz , bu doğru.
mklement0

202

Kabul edilen cevaba eklemek için:

Aklınızda bulundurun set -ebazen boruları var özellikle eğer yeterli değildir.

Örneğin, bu komut dosyasının olduğunu varsayalım

#!/bin/bash
set -e 
./configure  > configure.log
make

... beklendiği gibi çalışır: configureyürütmede bir hata .

Yarın görünüşte önemsiz bir değişiklik yapacaksınız:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

... ve şimdi çalışmıyor. Bu burada açıklanır ve bir geçici çözüm (yalnızca Bash) sağlanır:

#! / Bin / bash
set -e 
set -o boru arızası

./configure | tee configure.log
Yapmak

1
Birlikte olmak zorunda pipefailolmanın önemini açıkladığınız için teşekkür ederiz set -o!
Malcolm

83

Örneğinizdeki if ifadeleri gereksizdir. Sadece şu şekilde yapın:

dosomething1 || exit 1

Ville Laurikari'nin tavsiyelerini alıp kullanırsanız set -e, bazı komutlar için bunu kullanmanız gerekebilir:

dosomething || true

|| trueKomut boru hattı bir var yapacak true, böylece komut başarısız olsa bile dönüş değeri -eseçeneği senaryoyu öldürmez.


1
Bunu severim. Özellikle üst yanıt bash merkezli olduğu için (zsh betikleri için ne ölçüde geçerli olup olmadığı bana açık değil). Ve bakabilirim, ama senin daha açık, çünkü mantık.
g33kz0r

set -ebas merkezli değildir - orijinal Bourne Kabuğunda bile desteklenir.
Marcos Vives Del Sol

27

Çıkışta temizlemeniz gerekiyorsa, sahte sinyal ERR ile 'tuzak' da kullanabilirsiniz. Bu, INT'yi veya başka bir sinyali yakalamakla aynı şekilde çalışır; sıfır dışında bir değerden herhangi bir komut çıkarsa bash ERR atar:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

Veya, özellikle "set -e" kullanıyorsanız, EXIT'i yakalayabilirsiniz; betiğiniz daha sonra komut dosyası normal bir son, kesmeler, -e seçeneğinin neden olduğu bir çıkış vb. herhangi bir nedenle çıktığında yürütülür.


12

$?Değişken nadiren gereklidir. Yalancı deyim command; if [ $? -eq 0 ]; then X; fidaima olarak yazılmalıdır if command; then X; fi.

Gerekli olan durumlar $?, birden fazla değere karşı kontrol edilmesi gerektiğidir:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

veya $?yeniden kullanılması veya başka bir şekilde manipüle edilmesi gerektiğinde:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

3
Neden "daima" olarak yazılmalıdır? " Yani, neden " olmalıdır öyle olsun"? Bir komut uzun olduğunda (GCC'yi bir düzine seçenekle çağırmayı düşünün), dönüş durumunu kontrol etmeden önce komutu çalıştırmak çok daha okunabilir.
ysap

Bir komut çok uzunsa, adlandırarak parçalayabilirsiniz (kabuk işlevi tanımlayın).
Mark Edgar

12

İle çalıştırın -eveya set -eüstünde.

Ayrıca bak set -u.


34
Potansiyel olarak başkalarını kaydetmek için aşağıdakileri okuma ihtiyacı help set: -uayarlanmamış değişkenlere yapılan referansları hata olarak kabul eder.
mklement0

1
yani her ikisi de değil ya set -uda set -e, değil mi? @lumpynose
ericn

1
@eric Birkaç yıl önce emekli oldum. İşimi sevmeme rağmen yaşlı beynim her şeyi unuttu. Her ikisini birlikte kullanabileceğinizi tahmin ediyorum; benim açımdan kötü ifadeler; "Ve / veya" demeliydim.
lumpynose

3

Gibi bir ifade

dosomething1 && dosomething2 && dosomething3

komutlardan biri sıfır dışında bir değerle döndüğünde işlemeyi durdurur. Örneğin, aşağıdaki komut hiçbir zaman "tamam" yazmaz:

cat nosuchfile && echo "done"
echo $?
1


-2

Mark Edgars'ın girişine ek bir soru olduğu için burada başka bir referans atmak ve işte ek bir örnek ve genel olarak konuya değinmek:

[[ `cmd` ]] && echo success_else_silence

ki bu cmd || exit errcodebirisinin gösterdiği gibi.

Örneğin. Bir bölümün takılıysa sökülmesini sağlamak istiyorum:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 

5
Hayır, [[ cmd`]] `aynı şey değildir. Komutun çıkış durumu ne olursa olsun, komutun çıktısı boş ve doğruysa yanlıştır.
Gilles 'SO- kötü olmayı bırak'
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.