Bash komut dosyasında hata yükseltme


109

Bash komut dosyasında "Test durumları Başarısız!" Mesajıyla bir hata oluşturmak istiyorum. Bash'de bu nasıl yapılır?

Örneğin:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi

2
Bu hatada ne olmasını istiyorsunuz? Senaryonun adı nasıl? Sadece bir komut dosyası mı yoksa birçok komut dosyası mı? Senaryonuzun kullanım alanları nasıl görünecek?
Etan Reisner

sadece bir senaryo. ./Script/test.sh gibi bir ubuntu terminalini kullanarak aradım
Naveen Kumar



Aşk yok echo you screwed up at ... | mail -s BUG $bugtrackeremailaddressmu?
eklenmiştir

Yanıtlar:


126

Bu, hata mesajının nerede saklanmasını istediğinize bağlıdır.

Aşağıdakileri yapabilirsiniz:

echo "Error!" > logfile.log
exit 125

Veya şu:

echo "Error!" 1>&2
exit 64

Bir istisna oluşturduğunuzda, programın çalışmasını durdurursunuz.

İşletim sistemine döndürmek isteyebileceğiniz hata kodunun exit xxxnerede xxxolduğu gibi bir şey de kullanabilirsiniz (0'dan 255'e kadar). İşte 125ve 64bunlarla çıkabileceğiniz rastgele kodlar. Program (örn. Bir hata oluştu) anormal durdu OS göstermek için gerektiğinde, bir geçmesi gerekiyor sıfır olmayan çıkış kodu için exit.

@Chepner'ın da belirttiği gibi , yapabilirsiniz exit 1, bu da belirsiz bir hata anlamına gelir .


12
veya hataların gitmesi gereken stderr'e gönderebilirsiniz.

stderr'e nasıl gönderilir?
Naveen Kumar

2
@ user3078630, cevabımı az önce düzenledim. 1>&2hile yapacak
ForceBru

Bunun bir hata olduğuna varsa, gereken sıfırdan farklı çıkış durumu ile çıkmak da. exitkendi başına en son tamamlanan komutun çıkış durumunu kullanır, bu 0 olabilir.
chepner

3
Aklınızda belirli bir anlam olmadıkça, sadece kullanmalısınız exit 1, bu da geleneksel olarak belirtilmemiş bir hata anlamına gelir.
chepner

38

Temel hata işleme

Test çalışması çalıştırıcınız başarısız testler için sıfırdan farklı bir kod döndürürse , basitçe şunu yazabilirsiniz:

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

Veya daha da kısa:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

Veya en kısası:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

Test_handler'ın çıkış koduyla çıkmak için:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

Gelişmiş hata işleme

Daha kapsamlı bir yaklaşım benimsemek istiyorsanız, bir hata işleyiciniz olabilir:

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

ardından test olayınızı çalıştırdıktan sonra onu çağırın:

run_test_case test_case_x
exit_if_error $? "Test case x failed"

veya

run_test_case test_case_x || exit_if_error $? "Test case x failed"

Bir hata işleyiciye sahip olmanın avantajları exit_if_errorşunlardır:

  • günlüğe kaydetme , yığın izleme yazdırma , bildirim, temizleme yapma gibi tüm hata işleme mantığını tek bir yerde standartlaştırabiliriz
  • hata işleyicisinin hata kodunu bir argüman olarak almasını sağlayarak, çağıranı ifhatalar için çıkış kodlarını test eden blokların karmaşasından kurtarabiliriz.
  • Eğer bir sinyal işleyicimiz varsa ( tuzak kullanarak ), oradan hata işleyiciyi çağırabiliriz

Hata işleme ve günlük kaydı kitaplığı

İşte hata işleme ve günlüğe kaydetmenin eksiksiz bir uygulaması:

https://github.com/codeforester/base/blob/master/lib/stdlib.sh


İlgili Mesajlar


9

Bu soruna yaklaşmanın birkaç yolu daha var. İhtiyaçlarınızdan birinin, birkaç kabuk komutu içeren bir kabuk betiği / işlevi çalıştırmak ve betiğin başarılı bir şekilde çalışıp çalışmadığını kontrol etmek ve hata durumunda hata atmak olduğunu varsayarsak.

Kabuk komutları genellikle, kabuğun bazı beklenmedik olaylar nedeniyle başarılı olup olmadığını bilmesini sağlamak için döndürülen çıkış kodlarına dayanır.

Yani, yapmak istediğiniz şey bu iki kategoriye

  • hata durumunda çıkmak
  • çıkış ve hata durumunda temizleme

Hangisini yapmak istediğinize bağlı olarak, kullanabileceğiniz kabuk seçenekleri vardır. İlk durumda, kabuk ile bir seçenek sunar set -eve saniye bir yapabileceğini trapüzerindeEXIT

exitKomut dosyamda / işlevimde kullanmalı mıyım ?

Genel olarak kullanmak exitokunabilirliği artırır Bazı rutinlerde, cevabı öğrendikten sonra, arama rutininden hemen çıkmak istersiniz. Rutin, bir hata tespit ettiğinde daha fazla temizleme gerektirmeyecek şekilde tanımlanmışsa, hemen çıkmamak, daha fazla kod yazmanız gerektiği anlamına gelir.

Yani durumlarda bunu tercih edilir, senaryonun sonlandırılması temiz hale getirmek için senaryo üzerinde temizlik eylemleri yapmak gerekirse değil kullanımına exit.

set -eÇıkışta hata için kullanmalı mıyım ?

Hayır!

set -ekabuğa "otomatik hata algılama" ekleme girişimiydi. Amacı, herhangi bir hata oluştuğunda kabuğun iptal edilmesine neden olmaktı, ancak birçok potansiyel tuzakla birlikte geliyor, örneğin,

  • Bir if testinin parçası olan komutlar bağışıktır. Örnekte, testmevcut olmayan dizindeki denetimi bozmasını beklerseniz, bu olmaz, else durumuna geçer.

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
    
  • Sonuncusu dışındaki bir ardışık düzen içindeki komutlar bağışıktır. Aşağıdaki örnekte, en son çalıştırılan (en sağdaki) komutun çıkış kodu kabul catedildiğinden ( ) ve başarılı olmuştur. Bu, set -o pipefailseçeneğe göre ayarlanarak önlenebilir, ancak yine de bir uyarıdır.

    set -e
    somecommand that fails | cat -
    echo survived 
    

Kullanım için önerilir - trapçıkışta

Karar, sözde sinyali set -ekullanmak yerine, körü körüne çıkmak yerine bir hatayı işleyebilmek istiyorsanız trap, ERRsözde sinyali kullanmaktır.

ERRTuzak kabuk kendisi sıfır olmayan bir hata kodu ile çıktığında kod çalıştırmak, bunlarla değilken, bir durumun parçası (eğer benzer yapıda olmadığı için kabuk tarafından herhangi bir komut tarafından işletilen cmdya da cmd ||sıfır olmayan bir çıkış durumu ile) çıkar .

Genel uygulama, hangi satırda ve çıkışa neyin neden olduğu hakkında ek hata ayıklama bilgisi sağlamak için bir tuzak işleyicisi tanımlıyoruz. Sinyale neden olan son komutun çıkış kodunun ERRbu noktada hala mevcut olacağını unutmayın.

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

ve bu işleyiciyi, başarısız olan betiğin üstünde aşağıdaki gibi kullanıyoruz

trap cleanup ERR

Bunu false15. satırda içeren basit bir komut dosyası üzerinde bir araya getirerek, elde edeceğiniz bilgileri

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

trapAyrıca sadece kabuk tamamlanmasından (örneğin sizin kabuk komut dosyası çıkışların) üzerinde temizleme çalışmasına seçenekleri sinyali, hata bakılmaksızın sağlar EXIT. Aynı anda birden fazla sinyali yakalayabilirsiniz. Yakalanacak desteklenen sinyallerin listesi trap.1p - Linux kılavuz sayfasında bulunabilir.

Dikkat edilmesi gereken başka bir şey de, alt kabuklarla uğraşıyorsanız, sağlanan yöntemlerden hiçbirinin işe yaramayacağını anlamaktır; bu durumda, kendi hata işlemenizi eklemeniz gerekebilir.

  • Bir alt kabukta set -eişe yaramaz. falseAlt kabuk ile sınırlıdır ve ana kabuğuna yayılır geçmez. Hata işlemeyi burada yapmak için kendi mantığınızı ekleyin(false) || false

    set -e
    (false)
    echo survived
    
  • Aynı şey için trapde geçerlidir. Aşağıdaki mantık, yukarıda belirtilen nedenlerden dolayı işe yaramaz.

    trap 'echo error' ERR
    (false)
    

5

İşte STDERR'de başarısız olan her şeyin son bağımsız değişkenini yazdıran, başarısız olduğu satırı bildiren ve çıkış kodu olarak satır numarasıyla komut dosyasından çıkan basit bir tuzak. Bunların her zaman harika fikirler olmadığını, ancak bunun üzerine inşa edebileceğiniz bazı yaratıcı uygulamaları gösterdiğini unutmayın.

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR

Bunu test etmek için bir döngü içeren bir komut dosyasına koydum. Sadece bazı rastgele sayılarda bir isabet olup olmadığını kontrol ediyorum; gerçek testler kullanabilirsiniz. Kefaletle serbest bırakmam gerekirse, atmak istediğim mesajla yanlış (tuzağı tetikleyen) diyorum.

Ayrıntılı işlevsellik için, tuzağın bir işleme işlevi çağırmasını sağlayın. Daha fazla temizleme, vb. Yapmanız gerekirse, arg ($ _) üzerinde her zaman bir case ifadesi kullanabilirsiniz. Biraz sözdizimsel şeker için bir var atayın -

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false

while :
do x=$(( $RANDOM % 10 ))
   case "$x" in
   0) $throw "DIVISION BY ZERO" ;;
   3) $raise "MAGIC NUMBER"     ;;
   *) echo got $x               ;;
   esac
done

Örnek çıktı:

# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6

Açıkçası yapabilirsin

runTest1 "Test1 fails" # message not used if it succeeds

Tasarımın iyileştirilmesi için çok yer var.

Bunun dezavantajları arasında falsegüzel olmadığı gerçeği (dolayısıyla şeker) ve tuzağa takılan diğer şeyler biraz aptal görünebilir. Yine de bu yöntemi seviyorum.


4

2 seçeneğiniz var: Komut dosyasının çıktısını bir dosyaya yeniden yönlendirin, Komut dosyasında bir günlük dosyasını tanıtın ve

  1. Çıktı bir dosyaya yeniden yönlendirme :

Burada, komut dosyasının uyarı ve hata mesajları dahil gerekli tüm bilgileri çıkardığını varsayarsınız. Daha sonra çıktıyı seçtiğiniz bir dosyaya yeniden yönlendirebilirsiniz.

./runTests &> output.log

Yukarıdaki komut hem standart çıktıyı hem de hata çıktısını günlük dosyanıza yönlendirir.

Bu yaklaşımı kullanarak, komut dosyasına bir günlük dosyası eklemeniz gerekmez ve bu nedenle mantık biraz daha kolaydır.

  1. Komut dosyasına bir günlük dosyası ekleyin :

Komut dosyanıza ya sabit kodlayarak bir günlük dosyası ekleyin:

logFile='./path/to/log/file.log'

veya bir parametre ile iletmek:

logFile="${1}"  # This assumes the first parameter to the script is the log file

Yürütme anında zaman damgasını komut dosyasının üst kısmındaki günlük dosyasına eklemek iyi bir fikirdir:

date '+%Y%-m%d-%H%M%S' >> "${logFile}"

Daha sonra hata mesajlarınızı günlük dosyasına yeniden yönlendirebilirsiniz

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
fi

Bu, hatayı günlük dosyasına ekleyecek ve yürütmeye devam edecektir. Kritik hatalar oluştuğunda yürütmeyi durdurmak istiyorsanız exit, komut dosyasını şunları yapabilirsiniz :

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
    # Clean up if needed
    exit 1;
fi

Not exit 1nedeniyle belirsiz bir hata olduğu program durdurma yürütme gösterir. İsterseniz bunu özelleştirebilirsiniz.

Bu yaklaşımı kullanarak günlüklerinizi özelleştirebilir ve komut dosyanızın her bileşeni için farklı bir günlük dosyasına sahip olabilirsiniz.


Nispeten küçük bir betiğiniz varsa veya bir başkasının komut dosyasını ilk yaklaşıma değiştirmeden çalıştırmak istiyorsanız daha uygundur.

Günlük dosyasının her zaman aynı konumda olmasını istiyorsanız, bu, 2 için daha iyi bir seçenektir. Ayrıca, birden çok bileşen içeren büyük bir komut dosyası oluşturduysanız, her bir bölümü farklı şekilde günlüğe kaydetmek isteyebilirsiniz ve ikinci yaklaşım sizin tek seçeneği.


3

Genellikle, hata mesajlarını işlemek için bir işlev yazmayı yararlı buluyorum, böylece kod genel olarak daha temiz.

# Usage: die [exit_code] [error message]
die() {
  local code=$? now=$(date +%T.%N)
  if [ "$1" -ge 0 ] 2>/dev/null; then  # assume $1 is an error code if numeric
    code="$1"
    shift
  fi
  echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
  exit $code
}

Bu, hata kodunu önceki komuttan alır ve tüm komut dosyasından çıkarken varsayılan hata kodu olarak kullanır. Ayrıca, desteklendiği yerlerde mikrosaniyelerle zamanı not eder (GNU tarihi %Nnanosaniyedir, daha sonra mikrosaniyelere kısaltabiliriz).

İlk seçenek sıfır veya pozitif bir tamsayı ise, çıkış kodu olur ve onu seçenekler listesinden kaldırırız. Sonra, hiç trankulatı örneğin kelime "HATA" ve zaman (biz olmayan GNU kez mikrosaniye için trankulatı nanosaniye parametre genişleme kullanın veya senaryosunu adıyla, standart hata mesajı rapor 12:34:56.%Netmek12:34:56 ). ERROR kelimesinden sonra iki nokta üst üste ve boşluk eklenir, ancak yalnızca sağlanan bir hata mesajı varsa. Son olarak, önceden belirlenen çıkış kodunu kullanarak komut dosyasından çıkıyoruz ve herhangi bir tuzağı normal şekilde tetikliyoruz.

Bazı örnekler (kodun içinde bulunduğunu varsayın script.sh):

if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"

$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"

$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
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.