İşlem değiştirmeyi kullanırken çıkış kodu / tanıtıcı hatalarını nasıl doğru şekilde yakalayabilirim?


13

SO üzerinde Q & A alınan aşağıdaki yöntemi kullanarak dosya adlarını bir diziye ayrıştırır bir komut dosyası var :

unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
    ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)

Bu harika çalışır ve her türlü dosya adı varyasyonunu mükemmel şekilde işler. Ancak, bazen, olmayan bir dosyayı betiğe geçireceğim, örneğin:

$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...

Normal şartlar altında komut dosyasının çıkış kodunu benzer bir şeyle yakalamasını ve RET=$?nasıl devam edeceğine karar vermek için kullanmasını isterdim . Bu, yukarıdaki işlem ikamesi ile işe yaramıyor gibi görünüyor.

Bu gibi durumlarda doğru prosedür nedir? Dönüş kodunu nasıl yakalayabilirim? Değiştirilen süreçte bir şeyin yanlış olup olmadığını belirlemenin daha uygun yolları var mı?

Yanıtlar:


5

Herhangi bir alt kabuklu işlemden geri dönüşü stdout'una geri göndererek kolayca alabilirsiniz. Aynı şey proses ikamesi için de geçerlidir:

while IFS= read -r -d $'\0' FILE || 
    ! return=$FILE
do    ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")

- O zaman çok mu son satırı o çalıştırırsanız (veya \0dava edilebilir ayrılmış bölümü) olacak find'in dönüş durumu. readbir EOF aldığında 1'e dönecektir - bu nedenle $returnayarlanan tek zaman $FILEen son okunan bilgi bitidir.

Kullandığım printfextra eklemesini tutmak için \newline - hatta çünkü bu önemli readdüzenli gerçekleştirilen - Bir hangi üzerinde sınırlandırmaktadır yok \0, boş karakterler - bu sadece okuma veri bitmeyen zaman durumlarda 0 dışında dönmek için gidiyor bir \newline. Dolayısıyla, son satırınız bir \newline ile bitmezse, okuma değişkeninizdeki son değer geri dönüşünüz olacaktır.

Yukarıdaki komutu ve ardından:

echo "$return"

ÇIKTI

0

Ve eğer işlem değiştirme kısmını değiştirirsem ...

...
done < <(! find . -type f -print0; printf "$?")
echo "$return"

ÇIKTI

1

Daha basit bir gösteri:

printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done

ÇIKTI

pipe

Ve aslında, istediğiniz geri dönüş, süreç ikamesi içinden stdout'a yazdığınız son şey olduğu sürece - veya bu şekilde okuduğunuz herhangi bir alt kabuklu işlem - o $FILEzaman her zaman istediğiniz geri dönüş durumu olacaktır bitti. Ve böylece || ! return=...parça kesinlikle gerekli değildir - sadece kavramı göstermek için kullanılır.


5

Süreç ikamesindeki süreçler eşzamansızdır: kabuk bunları başlatır ve daha sonra ne zaman öldüklerini tespit etmek için herhangi bir yol vermez. Böylece çıkış durumunu alamazsınız.

Çıkış durumunu bir dosyaya yazabilirsiniz, ancak genel olarak beceriksizdir çünkü dosyanın ne zaman yazıldığını bilemezsiniz. Burada, döngü bittikten hemen sonra dosya yazılır, bu yüzden beklemek mantıklıdır.

 < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)

Başka bir yaklaşım, adlandırılmış bir boru ve bir arka plan işlemi kullanmaktır (bunun için yapabilirsiniz wait).

mkfifo find_pipe
find  >find_pipe &
find_pid=$!
 <find_pipe
wait $find_pid
find_status=$?

Her iki yaklaşım da uygun değilse, Perl, Python veya Ruby gibi daha yetenekli bir dile gitmeniz gerektiğini düşünüyorum.


Bu cevap için teşekkürler. Tanımladığınız yöntemler işe yarıyor, ancak tahmin ettiğimden biraz daha karmaşık olduklarını itiraf etmeliyim. Benim durumumda, tüm argümanlar üzerinden yinelenen ve bunlardan biri bir dosya veya klasör değilse bir hata yazdırılan soruda gösterilen döngüden önce bir döngüye yerleştim. Bu, değiştirilen işlemde oluşabilecek diğer hata türlerini ele almasa da, bu özel durum için yeterince iyidir. Böyle durumlarda daha karmaşık bir hata işleme yöntemine ihtiyacım olursa kesinlikle cevabınıza geri döneceğim.
Glutanimate

2

Bir yardımcı işlem kullanın . Yerleşik coprocaracı kullanarak bir alt işlem başlatabilir, çıktısını okuyabilir ve çıkış durumunu kontrol edebilirsiniz:

coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?

Dizin yoksa, waitsıfır olmayan bir durum kodu ile çıkar.

Şu anda PID'yi başka bir değişkene kopyalamak gerekiyor çünkü $LS_PIDdaha önce ayarlanmayacak wait. Ayrıntılar için coproc'u beklemeden önce Bash unsets * _PID değişkenine bakın.


1
Birinin okuma -u $ LS vs <& "$ LS" ne zaman kullanacağını merak ediyorum? - teşekkürler
Brian Chrisman

1
@BrianChrisman Bu durumda, muhtemelen asla. read -uaynı şekilde çalışmalıdır. Örnek jenerik olmak ve yardımcı işlem çıktısının başka bir komuta nasıl iletilebileceğini göstermekti.
Feuermurmel

1

Bir yaklaşım:

status=0
token="WzNZY3CjqF3qkasn"    # some random string
while read line; do
    if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
        status="${BASH_REMATCH[1]}"
    else
        echo "$line"
    fi
done < <(command; echo "$token:$?")
echo "Return code: $status"

Fikir, komut tamamlandıktan sonra çıkış durumunu rastgele belirteçle birlikte yankılamak ve ardından çıkış durumunu aramak ve ayıklamak için bash normal ifadelerini kullanmaktır. Belirteç, çıktıda aramak için benzersiz bir dize oluşturmak için kullanılır.

Muhtemelen bunu genel bir programlama anlamında yapmanın en iyi yolu değildir, ancak bash ile başa çıkmanın en az acı verici yolu olabilir.

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.