Pipo arızalarına benzer şekilde bastonun backtick arızasından çıkmasını nasıl sağlayabilirim?


55

Bu yüzden, hataları yakalamadığından emin olmak için baston scriptlerimi nerede (ve Python / Ruby gibi bir dile devredemezseniz) sertleştirmeyi seviyorum.

Bu damarda ben gibi şeyler içeren bir strict.sh var:

set -e
set -u
set -o pipefail

Ve diğer senaryolarda kaynak. Bununla birlikte, boru hattı arızalanırken:

false | echo it kept going | true

Kalmayacak:

echo The output is '`false; echo something else`' 

Çıktı

Çıktı ''

Yanlış, sıfır olmayan durumu ve no-stdout değerini döndürür. Bir boruda başarısız olabilirdi, ancak burada hata yakalanmadı. Bu aslında daha sonra değişkende saklanan bir hesaplama olduğunda ve değer boş olarak ayarlandığında, bu daha sonra sorunlara neden olabilir.

Yani - bir backtick içindeki sıfır olmayan bir dönüş kodunu çıkmak için yeterli bir sebep olarak ele almanın bir yolu var mı?

Yanıtlar:


51

Tek UNIX belirtiminde , anlamınıset -e tanımlamak için kullanılan tam dil :

Bu seçenek açık olduğunda , Kabuk Hatalarının Sonuçlarında listelenen sebeplerden herhangi biri için basit bir komut başarısız olursa veya bir çıkış durumu değeri> 0 döndürürse ve [koşullu ya da olumsuzlanan bir komut] değilse, kabuk derhal çıkar.

Bir kabukta böyle bir komut oluştuğunda ne olacağı konusunda bir belirsizlik vardır . Pratik bir bakış açısıyla, tüm alt kabukların çıkarması, çıkış yapmak ve sıfır olmayan bir durumu ana kabuğa döndürmektir. Üst kabuğun çıkıp çıkmayacağı, bu sıfır olmayan durumun ana kabukta başarısız olan basit bir komuta çevrilip çevrilmemesine bağlıdır.

Böyle bir sorunlu durum, karşılaştığınız durumdur: bir komut değişikliğinden sıfır olmayan dönüş durumu . Bu durum göz ardı edildiğinden, ana kabuğun çıkmasına neden olmaz. Daha önce keşfetmiş olduğunuz gibi , çıkış durumunu hesaba katmanın bir yolu, komut yerine kullanılan seçeneği basit bir atamada kullanmaktır : ödevin çıkış durumu, ödev (ler) deki son komut yerine koymanın çıkış durumudur .

Bunun, yalnızca son bir değişimin durumu hesaba katıldığı için yalnızca tek bir komut değişikliğinde olması gerektiği gibi gerçekleştirileceğini unutmayın. Örneğin, aşağıdaki komut başarılıdır (hem standarda göre hem de gördüğüm her uygulamada):

a=$(false)$(echo foo)

İzlemek için başka durum açıkça altkabuklarda : (somecommand). Yukarıdaki yoruma göre, alt kabuk sıfır olmayan bir durum döndürebilir, ancak bu ana kabuktaki basit bir komut olmadığından ana kabuk devam etmelidir. Aslında, tanıdığım tüm mermiler ebeveynleri bu noktada geri döndürüyor. Bu, (cd /some/dir && somecommand)parantezlerin geçerli bir dizin değişikliği gibi bir işlemi tutmak için kullanıldığı yerlerde birçok durumda yararlı olsa set -eda, alt kabukta kapatılmışsa veya alt kabuk sıfırdan farklı bir durumda döndürürse belirtimi ihlal eder . !gerçek bir komutla kullanmak gibi onu sonlandırmaz . Örneğin, tüm kül, bash, pdksh, ksh93 ve zsh foo, aşağıdaki örneklerde gösterilmeden çıkar:

set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"

Ancak set -e, yürürlükte iken hiçbir basit komut başarısız olmadı !

Üçüncü bir sorunlu durum, önemsiz bir boru hattındaki unsurlardır . Uygulamada, tüm kabuklar, boru hattı elemanlarının sonuncusu dışındaki başarısızlıklarını görmezden gelir ve son boru hattı elemanına ilişkin iki davranıştan birini sergiler:

  • Boru hattının son elemanını üst kabukta yürüten ATT ksh ve zsh, her zamanki gibi iş yapar: eğer boru hattının son elemanında basit bir komut başarısız olursa, ana komutu olan bu komutu uygulayan kabuk, çıkışlar.
  • Diğer kabuklar, boru hattının son elemanı sıfır olmayan bir durum döndürürse çıkarak davranışı yaklaşık olarak tahmin eder.

Daha önce set -eolduğu gibi, boru hattının son elemanında bir olumsuzlamayı kapatmak veya kullanmak, kabuğunu sonlandırmayacak şekilde sıfır olmayan bir durum döndürmesine neden olur; ATT ksh ve zsh dışındaki mermiler daha sonra çıkacaktır.

Bash'in pipefailseçeneği set -e, elemanlarından herhangi birinin sıfırdan farklı bir durum döndürmesi durumunda bir boru hattının derhal çıkmasına neden olur .

Başka bir komplikasyon olarak, set -ePOSIX modunda olmadığı sürece ( set -o posixveya POSIXLY_CORRECTbash başladığında çevrede olan) bash'in alt kabuklarda kapandığını unutmayın .

Bütün bunlar POSIX şartnamesinin ne yazık ki -eseçeneği belirlemek konusunda kötü bir iş çıkardığını göstermektedir . Neyse ki, mevcut kabuklar davranışlarında çoğunlukla tutarlıdır.


Bunun için teşekkürler. Sahip olduğum bir deneyim, bash'ın sonraki sürümlerinde bazı hataların yakalandığı, önceki sürümlerde set -e ile göz ardı edildiği idi. Burada niyetim, işlenmeyen herhangi bir hata dönüş / başarısızlık koşulunun bir komut dosyasının çıkmasına neden olacağı ölçüde komut dosyalarını sertleştirmektir. Bu, atık çıktı dosyalarını ve yanlış bir env ile yarım saatlik bir kaostan sonra "0" mutlu bir çıkış kodunu ürettiği bilinen eski bir sistemde - bir şahin gibi çıktı izlemiyorsanız (ve tüm hataları değil) stderr'da, bazıları stdout'ta, bazı kısımları / dev / null'd), sadece bilmiyorsunuz.
Danny Staple,

"Örneğin, tüm kül, bash, pdksh, ksh93 ve zsh, aşağıdaki örneklerde foo görüntülemeden çıkıyor": BusyBox külü bu şekilde davranmaz, çıktıyı belirtime göre görüntüler. (. BusyBox 1.19.4 ile test edilmiştir) Nor ile o çıkış yapar set -e; (cd /nonexisting).
dubiousjim

27

(Bir çözüm bulduğum için kendime cevap ver) Bir çözüm, bunu daima bir ara değişkene atamak. Bu yolla returncode ( $?) ayarlanır.

Yani

ABC=`exit 1`
echo $?

Bununla birlikte, çıktısı alınacak 1(veya varsa set -eçıkacak)

echo `exit 1`
echo $?

Will çıkışı 0boş bir satırdan sonra. Echo'nun (veya backtick çıktısıyla çalışan diğer komutların) dönüş kodu, 0 dönüş kodunun yerine geçecektir.

Hala orta değişken gerektirmeyen çözümlere açığım ama bu bana bir şekilde yaklaşıyor.


23

OP'nin kendi cevabında işaret ettiği gibi, alt komutun çıktısını bir değişkene atamak problemi çözer; $?yarasız bırakılır.

Bununla birlikte, bir uç durum sizi yanlış negatif (yani komut başarısız ancak hata kabarmaz) ile olumsuz bir bulmacada bulabilir, localdeğişken bildirimi:

local myvar=$(subcommand)her zaman geri döner 0!

bash(1) şunu belirtir:

   local [option] [name[=value] ...]
          ... The return status is 0 unless local is used outside a function,
          an invalid name is supplied, or name is a readonly variable.

İşte basit bir test durumu:

#!/bin/bash

function test1() {
  data1=$(false) # undeclared variable
  echo 'data1=$(false):' "$?"
  local data2=$(false) # declaring and assigning in one go
  echo 'local data2=$(false):' "$?"
  local data3
  data3=$(false) # assigning a declared variable
  echo 'local data3; data3=$(false):' "$?"
}

test1

Çıktı:

data1=$(false): 1
local data2=$(false): 0
local data3; data3=$(false): 1

teşekkür ederim, aradaki tutarsızlık VAR=...ve local VAR=...beni gerçekten etkiledi!
Jonny,

13

Diğerlerinin söylediği gibi, localher zaman 0 döndürür. Çözüm önce değişkeni açıklamaktır:

function testcase()
{
    local MYRESULT

    MYRESULT=$(false)
    if (( $? != 0 )); then
        echo "False returned false!"
        return 1
    fi

    return 0
}

Çıktı:

$ testcase
False returned false!
$ 

4

Bir komut değiştirme hatasından çıkmak için açıkça şöyle -ebir alt kabuk oluşturabilirsiniz:

set -e
x=$(set -e; false; true)
echo "this will never be shown"

1

İlginç nokta!

Buna hiç rastlamadım, çünkü arkadaşım değilim set -e(onun yerine tercih ettim trap ... ERR) ama çoktan sınamıştım: trap ... ERRaynı zamanda $(...)(veya eski moda backticks) içindeki hataları da yakalamayın .

Bence sorun (sık sık olduğu gibi) burada bir alt kabuk denir ve -eaçıkça mevcut kabuk anlamına gelir .

Şu anda akla gelen diğer bir çözüm, okumayı kullanmak olacaktır:

 ls -l ghost_under_bed | read name

Bu atar ERRve -ekabukla sonlandırılır. Tek sorun: bu yalnızca tek bir çıktı satırı olan komutlar için çalışır (veya satırları birleştiren bir şeyi aktarırsınız).


1
Okuma hilesinden emin değilsiniz, denemeye çalışmak değişkenin bağlı olmamasına yol açar. Bunun, borunun diğer tarafının etkin bir alt kabuk olması nedeniyle olabileceğini düşünüyorum, ad kullanılamayacak.
Danny Staple
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.