“-O errtrace” kullanarak komut değiştirme işleminde bindirme hataları (yani set -E)


14

Bu ref kılavuzuna göre :

-E (ayrıca -o errtrace)

Ayarlanırsa, ERR üzerindeki herhangi bir tuzak kabuk işlevleri, komut ikameleri ve alt kabuk ortamında yürütülen komutlar tarafından devralınır. ERR tuzağı normalde bu gibi durumlarda kalıtsal değildir.

Ancak, yanlış yorumlamalıyım, çünkü aşağıdakiler işe yaramıyor:

#!/usr/bin/env bash
# -*- bash -*-

set -e -o pipefail -o errtrace -o functrace

function boom {
  echo "err status: $?"
  exit $?
}
trap boom ERR


echo $( made up name )
echo "  ! should not be reached ! "

Zaten basit atama biliyorum my_var=$(made_up_name), ile komut dosyası set -eçıkacaktır (yani errexit).

Is -E/-o errtraceyukarıdaki kodu gibi işin gerekiyordu? Ya da büyük olasılıkla yanlış okudum mu?


2
Bu iyi bir soru. Değiştirme echo $( made up name )ile $( made up name )istenilen davranışı üretir. Yine de bir açıklamam yok.
iruvar

Ben bash -E hakkında bilmiyorum ama ben sadece bir hata bir boru hattındaki son komuttan kaynaklanıyorsa -e bir kabuk çıkış etkiler biliyorum. Bu nedenle, sizin var=$( pipe )ve $( pipe )örneklerinizin her ikisi de boru uç noktalarını temsil eder, oysa pipe > echodeğil. Benim man sayfam şöyle diyor: "1. Çok
komutlu bir

Yine de başarısız olabilirsiniz: echo $ ($ {madeupname?}). Ama bu ayarlandı -e. Yine, -E kendi deneyimimin dışında.
mikeserv

@mikeserv @ 1_CR Bash @ @ echoecho her zaman 0 döndürdüğünü gösterir. Bu, analizde hesaba

Yanıtlar:


5

Not: zshBuradaki örneklerin çoğu için "satır içi yorumları" kabul edecek şekilde yapılandırmazsanız ve yaptığım gibi bir proxy kabuğu aracılığıyla çalıştırmazsanız "kötü desenler" den şikayet eder sh <<-\CMD.

Tamam, yukarıdaki yorumlarda belirttiğim gibi, özellikle bashset -E hakkında bilmiyorum , ancak POSIX uyumlu kabukların bir değeri test etmek için basit bir yol sağladığını biliyorum:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
        echo "echo still works" 
    }
    _test && echo "_test doesnt fail"
    # END
    CMD
sh: line 1: empty: error string
+ echo

+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail

Eğer kullandığım olsa göreceksiniz Üstü parameter expansiontest etmek ${empty?} _test()hala returns sonuncu evinced gibi - bir pas echoBu başarısız değer öldürür çünkü oluşur $( command substitution )- onu içeren altkabuk, ancak üst kabuk _testşu anda - Kamyon tutar. Ve echoumursamıyor - 's bol mutlu bir tek hizmet etmek \newline; echoolduğunu değil bir test.

Ancak şunu düşünün:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    _test ||\
            echo "this doesnt even print"
    # END
    CMD
_test+ sh: line 1: empty: function doesnt run

_test()'sÖnceden değerlendirilmiş bir parametreyle girdiyi beslediğim için, INIT here-documentşimdi _test()işlev hiç çalışmaya çalışmaz. Dahası, shkabuk görünüşe göre hayaleti tamamen bırakıyor ve echo "this doesnt even print" hatta yazdırmıyor.

Muhtemelen istediğin bu değil .

Bunun nedeni ${var?}tarzı parametre genişleme olduğunu çıkmak üzere tasarlanmışshell bir eksik parametre durumunda bunun, bu gibi çalışır :

${parameter:?[word]}

Eğer Hata gösterir Nullveya Unset.parametre ayarlanmadan ya da boşsa, expansion of wordolacaktır (bu işaret eder ya da bir mesajı kelimesi atlanırsa ayarlanmadan) written to standard errorve shell exits with a non-zero exit status. Aksi takdirde, değeri parameter shall be substituted. Etkileşimli bir kabuktan çıkmak gerekmez.

Tüm belgeyi kopyalamayacağım / yapıştırmayacağım, ancak bir set but nulldeğer için başarısızlık istiyorsanız formu kullanın:

${var :? error message }

İle :colonyukarıdaki gibi. Bir nulldeğerin başarılı olmasını istiyorsanız , iki nokta üst üste işaretini atlayın. Ayrıca, bir anda göstereceğim gibi, bunu reddedebilir ve yalnızca ayarlanmış değerler için başarısız olabilirsiniz.

Başka bir koşusu _test():

    sh <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    echo "this runs" |\
        ( _test ; echo "this doesnt" ) ||\
            echo "now it prints"
    # END
    CMD
this runs
sh: line 1: empty: function doesnt run
now it prints

Bu, her türlü hızlı testle çalışır, ancak yukarıda _test(), pipelinebaşarısızlıkların ortasından çalıştığını ve aslında command listalt kabuğun tamamen başarısız olduğunu görürsünüz , çünkü işlevdeki komutların hiçbiri veya aşağıdaki echoçalıştırma hiç olmaz, ancak şimdi yazdırıldığı için kolayca test edilebileceği de gösterilmiştir echo "now it prints" .

Şeytan ayrıntıda gizlenmiş, sanırım. Yukarıdaki durumda, çıkan kabuk komut dosyası değildir , _main | logic | pipelineancak ( subshell in which we ${test?} ) ||küçük bir sanal alan çağrılır.

Ve açık olmayabilir, ancak sadece ters durum veya sadece set=değerler için geçmek istiyorsanız , oldukça basittir:

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

Yukarıdaki örnek, POSIX parametre ikamesinin 4 formunun hepsinden ve bunların çeşitli :colon nullveya not nulltestlerinden yararlanır. Yukarıdaki bağlantıda daha fazla bilgi var ve işte yine .

Sanırım _testfonksiyon çalışmalarımızı da göstermeliyiz, değil mi? Sadece empty=somethingfonksiyonumuza bir parametre olarak (veya önceden herhangi bir zamanda) beyan ederiz :

    sh <<-\CMD
    _test() { echo $( echo ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?tested as a pass before function runs}
    INIT
    echo "this runs" >&2 |\
        ( empty=not_empty _test ; echo "yay! I print now!" ) ||\
            echo "suspiciously quiet"
    # END
    CMD
this runs
not_empty
echo still works
yay! I print now!

Bu değerlendirmenin tek başına durduğuna dikkat edilmelidir - başarısız olmak için ek test gerektirmez. Birkaç örnek daha:

    sh <<-\CMD
    empty= 
    ${empty?null, no colon, no failure}
    unset empty
    echo "${empty?this is stderr} this is not"
    # END
    CMD
sh: line 3: empty: this is stderr

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

Ve son olarak orijinal soruya geri dönüyoruz: bir $(command substitution)alt kabuktaki hatalar nasıl ele alınır ? Gerçek şu ki - iki yol var, ama ikisi de doğrudan değil. Sorunun özü, kabuğun değerlendirme sürecidir - kabuk genişletmeleri ( $(command substitution)kabuğun değerlendirme işleminde geçerli kabuk komutu yürütme işleminden daha erken gerçekleşir) - hatalarınız yakalanıp sıkışabildiği zamandır.

Operasyonların yaşadığı sorun, mevcut kabuğun hataları değerlendirdiği zaman, $(command substitution)alt kabuğun yerini almış olmasıdır - hiçbir hata kalmamıştır.

Peki iki yol nedir? Ya bunu $(command substitution)alt kabuğun içinde, onsuz yaptığınız gibi testlerle açıkça yaparsınız ya da sonuçlarını geçerli bir kabuk değişkenine alır ve değerini test edersiniz.

Yöntem 1:

    echo "$(madeup && echo \: || echo '${fail:?die}')" |\
          . /dev/stdin

sh: command not found: madeup
/dev/stdin:1: fail: die

    echo $?

126

Yöntem 2:

    var="$(madeup)" ; echo "${var:?die} still not stderr"

sh: command not found: madeup
sh: var: die

    echo $?

1

Bu, satır başına bildirilen değişken sayısına bakılmaksızın başarısız olur:

   v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"

sh: command not found: madeup
sh: v1: parameter not set

Ve dönüş değeriniz sabit kalır:

    echo $?
1

ŞİMDİ TUZAK:

    trap 'printf %s\\n trap resurrects shell!' ERR
    v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
    echo "${v1:?#1 - still stderr}" "${v2:?invisible}"

sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap

    echo $?
0

Örneğin, pratikte onun tam özellikleri: echo $ {v = $ (madeupname)}; echo "$ {v:? trap1st}! 'a ulaşılamadı!"
2014'te

1
Özetlemek gerekirse: Bu parametre genişletmeleri, değişkenlerin vb echo "abc" "${v1:?}". Ve kabuk 1 değerini döndürür. Bu, komutla veya komut olmadan da ( "${v1:?}"doğrudan cli üzerinde) geçerlidir. Ancak OP'nin senaryosu için, tuzağı tetiklemek için gereken tek şey, değişken atamasını tek başına varolmayan bir komutun yerine koymasıdır. Aksi takdirde yankı davranışı, açıkladığınız testlerde olduğu gibi kesilmedikçe daima 0'a dönmektir.

Sadece v=$( madeup ). Neyin güvensiz olduğunu görmüyorum. Bu sadece bir görev, örneğin kişi komutu yanlış yazmış v="$(lss)". Hatalar. Evet, $? Komutunun hata durumunu doğrulayabilirsiniz. - çünkü satırdaki komuttur (komut adı olmayan bir atama) ve başka bir şey değildir - yankı için bir argüman değildir. Ayrıca, burada işlev =! 0 olarak sıkışmıştır, böylece iki kez geri bildirim alırsınız. Aksi takdirde, açıkladığınız gibi bunu bir çerçeve içinde düzenli bir şekilde yapmanın daha iyi bir yolu vardır, ancak OP'nin 1 tek satırı vardı: bir yankı artı başarısız ikamesi. Yankıyı merak ediyordu.

Evet, basit atama soruda belirtildiği gibi bir de belirtilmiştir ve -E'nin nasıl kullanılacağı konusunda özel tavsiyeler sunamasam da, gösterilen konuya benzer sonuçlar göstermeden mümkün olandan daha fazla olduğunu göstermeye çalıştım bashisms. Her durumda, özellikle söz konusu çözümlerin bir boru hattında ele alınmasını zorlaştıran, hat başına tek bir atama gibi bahsettiğiniz konular özellikle de nasıl ele alınacağını gösterdim. Ne söylediğiniz doğru olsa da - basitçe atamak konusunda güvensiz bir şey yoktur.
2014'te 28:14

1
İyi bir nokta - belki daha fazla çaba gerektirir. Bunu düşüneceğim.
14'te mikeserv

1

Ayarlanırsa, ERR üzerindeki herhangi bir tuzak kabuk işlevleri, komut değiştirme ve alt kabuk ortamında yürütülen komutlar tarafından devralınır

Senaryonuzda bir komut yürütme ( echo $( made up name )). Bash komutlarında şunlardan biri ile sınırlandırılır ; veya yeni bir çizgi ile . Komutta

echo $( made up name )

$( made up name ), komutun bir parçası olarak kabul edilir. Bu bölüm başarısız olsa ve hata ile geri dönse bile, tüm komut echobilmediği gibi başarıyla çalışır. Komut 0 ile döndüğünde hiçbir tuzak tetiklenmez.

İki komuta koymanız gerekir, atama ve yankı

var=$(made_up_name)
echo $var

echo $varbaşarısız olmaz - aslında bir $varbakmadan önce boşaltılır echo.
2014'te

0

Bunun nedeni bash'daki bir hata. Komut Değiştirme adımında

echo $( made up name )

madebir alt kabukta çalışır (veya bulunamaz), ancak alt kabuk üst kabuktan bazı tuzaklar kullanmayacak şekilde "optimize edilir". Bu, 4.4.5 sürümünde düzeltildi :

Belirli koşullar altında, bir çatalı ortadan kaldırmak için basit bir komut optimize edilir ve bu da bir EXIT tuzağının yürütülmemesine neden olur.

Bash 4.4.5 veya üstü ile aşağıdaki çıktıyı görmelisiniz:

error.sh: line 13: made: command not found
err status: 127
  ! should not be reached !

Tuzak işleyici beklendiği gibi çağrıldı, ardından alt kabuk çıkar. (set -e yalnızca alt kabuğun ebeveyne değil, çıkışına neden olur, böylece "ulaşılmaması gerekir" iletisine aslında ulaşılmalıdır.)

Eski sürümler için geçici bir çözüm, tam ve optimize edilmemiş bir alt kabuğun oluşturulmasını zorlamaktır:

echo $( ( made up name ) )

Ek boşlukların Aritmetik Genişletmeden ayırt edilmesi gerekir .

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.