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
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
echo you screwed up at ... | mail -s BUG $bugtrackeremailaddress
mu?
Yanıtlar:
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 xxx
nerede xxx
olduğu gibi bir şey de kullanabilirsiniz (0'dan 255'e kadar). İşte 125
ve 64
bunlarla çı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 .
1>&2
hile yapacak
exit
kendi başına en son tamamlanan komutun çıkış durumunu kullanır, bu 0 olabilir.
exit 1
, bu da geleneksel olarak belirtilmemiş bir hata anlamına gelir.
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; }
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:
if
hatalar için çıkış kodlarını test eden blokların karmaşasından kurtarabiliriz.İşte hata işleme ve günlüğe kaydetmenin eksiksiz bir uygulaması:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
__FILE__
, __LINE__
BashBu 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
Hangisini yapmak istediğinize bağlı olarak, kullanabileceğiniz kabuk seçenekleri vardır. İlk durumda, kabuk ile bir seçenek sunar set -e
ve saniye bir yapabileceğini trap
üzerindeEXIT
exit
Komut dosyamda / işlevimde kullanmalı mıyım ?Genel olarak kullanmak exit
okunabilirliğ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 -e
kabuğ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, test
mevcut 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 cat
edildiğinden ( ) ve başarılı olmuştur. Bu, set -o pipefail
seçeneğe göre ayarlanarak önlenebilir, ancak yine de bir uyarıdır.
set -e
somecommand that fails | cat -
echo survived
trap
çıkıştaKarar, sözde sinyali set -e
kullanmak yerine, körü körüne çıkmak yerine bir hatayı işleyebilmek istiyorsanız trap
, ERR
sözde sinyali kullanmaktır.
ERR
Tuzak 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 cmd
ya 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 ERR
bu 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 false
15. 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
trap
Ayrı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 -e
işe yaramaz. false
Alt 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 trap
de geçerlidir. Aşağıdaki mantık, yukarıda belirtilen nedenlerden dolayı işe yaramaz.
trap 'echo error' ERR
(false)
İş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 false
gü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.
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
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.
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 1
nedeniyle 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.
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 %N
nanosaniyedir, 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.%N
etmek12: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"