Bash betiğinde set -e ne anlama geliyor?


714

Ben bu paket Debian arşiv (.deb) dosyasından paketinden önce komut dosyası yürütür bu preinst dosya içeriğini inceliyorum .

Komut dosyası aşağıdaki koda sahiptir:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

İlk sorgum satırla ilgili:

set -e

Betiğin geri kalanının oldukça basit olduğunu düşünüyorum: Debian / Ubuntu paket yöneticisinin bir yükleme işlemi yürütüp yürütmediğini kontrol eder. Eğer öyleyse, uygulamamın sisteme yeni kurulmuş olup olmadığını kontrol eder. Varsa, komut dosyası "UygulamamAdı yeni yüklendi" iletisini yazdırır ve biter ( return 1bir "hata" ile biten anlamına gelir, değil mi?).

Kullanıcı Debian / Ubuntu paket sisteminden paketimi kurmasını istiyorsa, komut dosyası iki dizini de siler.

Bu doğru mu yoksa bir şey mi kaçırıyorum?



46
sorgunuzda google: -e ifadesinde bulamamanızın nedeni olumsuzlama olarak yorumlanır. Aşağıdaki sorguyu deneyin: bash set "-e"
Maleev

3
Ben kendime aynı soruyu sordunuz, ben baktığım @twalbergman set
Sedat Kılınç

4
nasıl kapatacağınızı düşünüyorsanız, kısa çizgiyi artı bir önekle değiştirin:set +e
Tom Saleeba

@twalberg ama gerçek insanlara sormak bir robot ;-) 'den bir talepte bulunmaktan çok daha ilginç.
vdegenne

Yanıtlar:


798

Gönderen help set:

  -e  Exit immediately if a command exits with a non-zero status.

Ancak bazı kişiler tarafından kötü uygulama olarak kabul edilir (bash SSS ve irc freenode #bash SSS yazarları). Kullanmanız önerilir:

trap 'do_something' ERR

do_somethinghata oluştuğunda işlevi çalıştırmak için .

Bkz. Http://mywiki.wooledge.org/BashFAQ/105


14
"Bir komut sıfırdan farklı bir durumdan çıkarsa hemen çık" ile aynı semantiği istersem do_something ne olurdu?
CMCDragonkai

71
trap 'exit' ERR
chepner

12
ERRTuzak Eğer işlevlere sahiptir, eğer öyleyse, kabuk işlevleri tarafından miras değil set -o errtraceveya set -Esadece bir kez tuzak izin ve küresel olarak uygulayacaktır.
ykay

31
gelmez trap 'exit' ERRyapmak şey farklıdır set -e?
Andy

22
kötü bir uygulama varsa neden Debian paketlerinde kullanılır ?
phuclv

98

set -ebir komut veya ardışık düzen bir hata içeriyorsa, komut dosyasının yürütülmesini durdurur; bu, komut dosyalarındaki hataları yok saymak olan varsayılan kabuk davranışının tersidir. Tip help setBu yerleşik komut için belgelere bakın bir terminalde.


45
Yürütmeyi yalnızca bir ardışık düzendeki son komutta bir hata varsa durdurur . set -o pipefailHataları yaymak için kullanılabilen Bash'e özgü bir seçenek vardır, böylece önceki komutlardan biri sıfırdan farklı bir durumdan çıkarsa boru hattı komutunun dönüş değeri sıfır değildir.
Anthony Geoghegan

2
Bunun -o pipefailyalnızca boru hattının ilk sıfır olmayan (yani terimlerle hata verme ) komutunun çıkış durumunun-o errexit sonuna yayıldığı anlamına geldiğini unutmayın. Boru hattındaki kalan komutlar hala koşmak bile, set -o errexit. Örneğin: echo success | cat - <(echo piping); echo continuesnerede echo successbaşarılı, ama yanılabilir komutu temsil yazdırır success, pipingve continuesfakat false | cat - <(echo piping); echo continuesbirlikte, falseyine de yazdırılacaktır, şimdi komutu temsil sessizce erroring pipingçıkmadan önce.
bb010g

55

Gereğince Bash - Set Yerleşik el eğer -e/ errexitayarlanır, kabuk çıkar hemen bir boru hattı tek bir kapsayan bir komutla , bir liste ya da bir bileşiğin bir komut getiri sıfır olmayan bir durumdur.

Varsayılan olarak, pipefailseçenek etkinleştirilmedikçe (varsayılan olarak devre dışıdır) , bir boru hattının çıkış durumu, boru hattındaki son komutun çıkış durumudur .

Öyleyse, sıfırdan farklı bir durumla çıkmak için boru hattının son (en sağdaki) komutun dönüş durumu veya tüm komutlar başarıyla çıkarsa sıfır.

Çıkışta bir şey yürütmek istiyorsanız, tanımlamayı deneyin trap, örneğin:

trap onexit EXIT

onexitaşağıda basit yığın izini basan aşağıdaki gibi bir şey yapmak için işleviniz nerede :

onexit(){ while caller $((n++)); do :; done; }

Bunun yerine ERR'de yakalanacak benzer bir seçenek -E/errtrace vardır, örneğin:

trap onerr ERR

Örnekler

Sıfır durum örneği:

$ true; echo $?
0

Sıfır olmayan durum örneği:

$ false; echo $?
1

Olumsuz durum örnekleri:

$ ! false; echo $?
0
$ false || true; echo $?
0

pipefailDevre dışı bırakılmış olarak test edin :

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

pipefailEtkinleştirildiğinde test et :

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

55

Nedeniyle bir komut dosyası için çıkış durumunun ne olduğunu anlamaya çalışırken bu yazı bulundu set -e. Cevap benim için çok açık değildi; dolayısıyla bu cevap. Temel olarak, set -ebir komutun yürütülmesini iptal eder (örneğin bir kabuk komut dosyası) ve başarısız olan komutun çıkış durumu kodunu döndürür (yani dış komut dosyası değil, iç komut dosyası) .

Örneğin, kabuk betiği olduğunu varsayalım outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

Kodu inner-test.sh:

#!/bin/sh
exit 26;

outer-script.shKomut satırından çalıştırdığımda , dış komut dosyam iç komut dosyasının çıkış koduyla sonlandırılıyor:

$ ./outer-test.sh
$ echo $?
26

10

Niyetin, söz konusu komut dosyasının hızlı bir şekilde başarısız olması olduğuna inanıyorum.

Bunu kendiniz test etmek set -eiçin bash istemine yazmanız yeterlidir . Şimdi koşmayı deneyin ls. Bir dizin listesi alacaksınız. Şimdi yazın lsd. Bu komut tanınmıyor ve bir hata kodu döndürecek ve böylece bash isteminiz kapanacak (nedeniyle set -e).

Şimdi bunu bir 'script' bağlamında anlamak için şu basit script'i kullanın:

#!/bin/bash 
# set -e

lsd 

ls

Olduğu gibi çalıştırırsanız, dizin listesini lsson satırın sonundan alırsınız . Eğer uncomment set -eve tekrar çalıştırırsanız, dizinin listeyi göremezsiniz çünkü bash hata ile karşılaştığında işlemeyi durdurur lsd.


Bu cevap, soru hakkında başkalarında henüz verilmemiş herhangi bir fikir veya bilgi ekliyor mu?
Charles Duffy

6
Sanırım diğer cevaplarda bulunmayan işlevsellik hakkında açık ve özlü bir açıklama sunuyor. Ek bir şey yok, diğer yanıtlardan daha odaklanmış.
Kallin Nagelberg

9

Bu eski bir soru ama burada cevapların hiçbiri kullanımını tartışmak set -eaka set -o errexitkomut dosyalarını işleme Debian paketinde. Debian politikası uyarınca, bu komut dosyalarında bu seçeneğin kullanılması zorunludur ; amaç, işlenmemiş bir hata durumu olasılığından kaçınmaktır.

Uygulamada bunun anlamı, çalıştırdığınız komutların hangi koşullar altında bir hata döndürebileceğini anlamanız ve bu hataların her birini açıkça işleyebilmenizdir.

Ortak yakalamalar örn. diff(Fark grepolduğunda bir hata döndürür ) ve (eşleşme olmadığında bir hata döndürür). Açık işleme ile hataları önleyebilirsiniz:

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Geçerli komut dosyasının adını mesaja dahil etmeye ve tanılama mesajlarını standart çıktı yerine standart hataya yazmaya nasıl dikkat ettiğimize de dikkat edin.)

Hiçbir açık işlem gerçekten gerekli veya yararlı değilse, açıkça hiçbir şey yapmayın:

diff this that || true
grep cat food || :

(Kabuğun :no-op komutunun kullanımı biraz belirsizdir, ancak oldukça yaygındır.)

Tekrarlamak gerekirse,

something || other

için kestirme

if something; then
    : nothing
else
    other
fi

yani açıkça otherve sadece somethingbaşarısız olursa çalıştırılması gerektiğini söylüyoruz . Longhand if(ve benzeri diğer kabuk akış kontrol deyimleri while, until) aynı zamanda bir hata işlemek için geçerli bir yoldur (gerçekten de olmasaydı, ile kabuk komut set -eakış kontrol beyanlarını ihtiva asla!)

Ve ayrıca, açık bir şekilde, böyle bir işleyicinin yokluğunda, bir fark bulunursa veya bir eşleşme bulamazsa , set -ekomut dosyasının tamamının bir hatayla hemen başarısız olmasına neden olur .diffgrep

Diğer yandan, bazı komutlar istediğiniz zaman hata çıkış durumu oluşturmaz. Genellikle sorunlu komutlar find(çıkış durumu dosyaların gerçekten bulunup bulunmadığını yansıtmaz) ve sed(çıkış durumu komut dosyasının herhangi bir girdi alıp almadığını veya gerçekten herhangi bir komutu başarıyla gerçekleştirip gerçekleştirmediğini göstermez). Bazı senaryolarda basit bir koruma, çıktı yoksa çığlık atan bir komuta boru bağlamaktır:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Bir boru hattının çıkış durumunun, o boru hattındaki son komutun çıkış durumu olduğuna dikkat edilmelidir. Yukarıdaki komutlar Yani aslında tamamen durumunu maske findve sedve sadece olmadığını söylemek grepnihayet başardı.

(Elbette Bash vardır set -o pipefail; ancak Debian paket komut dosyaları Bash özelliklerini kullanamaz. Politika sh, bu komut dosyaları için POSIX kullanımını kesin olarak belirler , ancak bu her zaman böyle değildir.)

Birçok durumda, bu defansif kodlama yaparken dikkat edilmesi gereken bir şeydir. Bazen, geçici bir dosyadan geçmeniz gerekir, böylece deyim ve rahatlık, aksi takdirde sizi bir kabuk boru hattı kullanmaya yönlendirecek olsa bile, bu çıktıyı üreten komutun başarıyla tamamlanıp tamamlanmadığını görebilirsiniz.


bu mükemmel bir cevap. ve en iyi uygulamayı teşvik eder. GREP komutundan tam olarak aynı sorunu yaşadım ve gerçekten 'set -e'yi kaldırmak istemedim
Minnie

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
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.