Bash sözdizimi hatası bir komut dosyasının yürütülmesini iptal nasıl?


15

Güvenli tarafta olmak için, bash bir sözdizimi hatasıyla karşılaşırsa bir komut dosyasının yürütülmesini iptal etmek istiyorum.

Şaşırtıcı şekilde, bunu başaramıyorum. ( set -eyeterli değildir.) Örnek:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Sonuç (bash-3.2.39 veya bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

$?Sözdizimi hatalarını yakalamak için her ifadeden sonra kontrol edemeyiz .

(Makul bir programlama dilinden böyle güvenli bir davranış bekliyordum ... belki de bu geliştiriciler için bir hata / dilek olarak rapor edilmelidir)

Daha fazla deney

if fark etmez.

Çıkarma if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Sonuç:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Belki de http://mywiki.wooledge.org/BashFAQ/105'teki egzersiz 2 ile ilgilidir ve bununla bir ilgisi vardır (( )). Ancak, bir sözdizimi hatası yürütmeye devam etmenin hala mantıksız olduğunu düşünüyorum.

Hayır, (( ))fark etmez!

Aritmetik test olmadan bile kötü davranır! Basit, basit bir script:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Sonuç:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

set -esözdizimi hatasının bir ififadede olduğu için yeterli değil . Başka herhangi bir yer betiği iptal etmelidir.
jordanm

@jordanm Tamam, bu neden set -eişe yaramadı bir açıklama olabilir . Ama sorum hala mantıklı. Herhangi bir sözdizimi hatası iptal edilebilir mi?
imz - Ivan Zakharyaschev

@jordanm "if" kaldırıldı; fark etmez (sorumu güncelledi).
imz - Ivan Zakharyaschev

Yanıtlar:


9

Bütünün bir fonksiyona sarılması işinizi görüyor gibi görünüyor:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Sonuç:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Neden bir fikrim yok - belki başka biri açıklayabilir?


2
Şimdi işlev tanımınız ayrıştırılıp değerlendiriliyor ve başarısız oluyor.
tripleee

Güzel çözüm! BTW, bu durumda da tüm programı iptal etmez. Ben eklenmiş ettik echo 'Bad2: has not aborted the execution after bad main!'(işlenen beklenen: senin Örneğin son olarak, ve çıktı şöyledir: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: çizgisini 6: #: sözdizimi hatası hata belirteci "#") Hatalı2: hatalı ana işlemden sonra yürütmeyi durdurmadı! $
imz - Ivan Zakharyaschev

Ama o zaman bir satır eklememeliyiz, her şeyi bir fonksiyonun içine koymalıyız.
imz - Ivan Zakharyaschev

@tripleee Evet, işlevin ayrıştırılması başarısız gibi görünüyor, bu yüzden tamamlanmadı, ancak tüm program aslında bu durumda iptal edilmedi (bu yüzden muhtemelen hataya çıkış yapmanın etkisi değildir).
imz - Ivan Zakharyaschev

6

Muhtemelen gerçek anlamı hakkında yanıltıcısınız set -e. Şovların çıktısının dikkatli bir şekilde okunması help set:

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

Yani -eçıkış durumu hakkında komutları değil komut sözdizimi hataları hakkında sıfırdan farklı olmanın.

Genel olarak, kullanımı kötü bir uygulama olarak kabul edilir set -e, çünkü tüm hatalar (yani, komutlardan sıfır olmayan tüm geri dönüşler) komut dosyası tarafından akıllıca ele alınmalıdır (sağlam bir komut dosyası düşünün, boşluk veya bir hipen ile başlar).

Sözdizimi hatasının türüne bağlı olarak, komut dosyası hiç yürütülmeyebilir. Bash'ta anlatacak kadar bilgili değilim tam olarak hangi sınıfta sözdizimi hataları sınıfı (sadece sınıflandırılmış olabilir) komut dosyasının hemen kürtaj yol açabilir ya da değil neden . Belki bir Bash gurusu katılacak ve her şeyi açıklığa kavuşturacaktır.

Umarım sadece set -e açıklamayı !

İsteğiniz hakkında:

Makul bir programlama dilinden böyle güvenli bir davranış bekliyordum ... belki de bu bir hata / geliştiricilere bash dilek olarak bildirilmelidir

Cevap kesinlikle hayır! gözlemlediğiniz gibi ( set -ebeklediğiniz gibi yanıt vermeyin) aslında çok iyi belgelenmiştir.


Demek istediğim böyle bir özelliğin olmaması bir problem. Odaklanmak istemedim set -e- hedeflerime biraz yakın, bu yüzden burada bahsedildi ve kullanıldı. Benim sorum değil set -e, sytax hatalarını durdurmak için yapılamazsa bash'ın güvensizliği ile ilgili. Ben her zaman sözdizimi hataları iptal yapmak için bir yol arıyorum.
imz - Ivan Zakharyaschev

4

Senaryoyu şöyle bir şey koyarak kontrol edebilirsin

bash -n "$0"

komut dosyasının üst kısmına yakın - set -eönemli bir kod parçasından sonra ama önce.

Bunun çok sağlam hissetmediğini söylemeliyim, ama eğer sizin için çalışıyorsa, belki de kabul edilebilir.


Mükemmel! Veya, olmadan set -e: bash -n "$0" || exit
Daniel S

0

İlk önce (( )) in bash aritmetik hesaplamalar olarak kullanılır, if ... use []for for kullanmayın .

İkincisi, ${a[#]} garip ve neden hata veriyor ... #herhangi bir dizi anlamı yok

Bununla ne yapmak istediğini bilmiyorum, ama alan sayısını bilmek istediğini varsayıyorum, bu yüzden istiyorsun ${#a[*]} bunun yerine

Son olarak, tamsayıları karşılaştırırken, (dizeler için kullanılır) -eqüzerinden önerilir ==. ==Ayrıca çalışacaktır ancak -eqtavsiye edilir.

böylece istiyorum:

if [ ${#a[*]} -eq 2 ]; then 

4
Bu doğru değil. ((Anahtar kelimeyi anahtar kelimeyle kullanmak yaygındır if. Örneğin if (( 5 * $b > 53 )),. Eski mermilerle taşınabilirliği hedeflemediğiniz sürece [[, genellikle tercih edilir [.

2
Evet, @Evan'a katılıyorum - [[ve ((özellikle "if" vb. İle kullanılacak hafif testler olarak tasarlandılar [.
imz - Ivan Zakharyaschev

1
[bir bash yapılıdır. Eski mermiler bunun kendi programı olmasını bekliyor.
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.