Tuzak kullanmak her zaman bir seçenek değildir. Örneğin, hata işlemeye ihtiyaç duyan ve herhangi bir komut dosyasından çağrılabilen (dosyayı yardımcı işlevlerle kaynakladıktan sonra) bir tür yeniden kullanılabilir işlev yazıyorsanız, bu işlev dış komut dosyasının çıkış süresi hakkında hiçbir şey kabul edemez, bu da tuzakları kullanmayı çok zorlaştırıyor. Tuzakları kullanmanın bir başka dezavantajı, arayan zincirinde daha önce ayarlanmış olabilecek önceki tuzağın üzerine yazma riski taşıdığından, kötü bir şekilde oluşturulabilirliktir.
Tuzaklar olmadan doğru hata işlemeyi yapmak için kullanılabilecek küçük bir numara var. Zaten diğer cevaplardan da bildiğiniz gibi , bir alt kabukta çalıştırsanız bile, onlardan sonra operatör set -e
kullanırsanız komutların içinde çalışmaz ||
; örneğin, bu işe yaramaz:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer
set -e
outer() {
echo '--> outer'
(inner) || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Ancak ||
temizlemeden önce dış fonksiyondan geri dönmesini önlemek için operatöre ihtiyaç vardır. Hile, iç komutu arka planda çalıştırmak ve hemen beklemek. wait
Yerleşik iç komutun çıkış kodu döndürür ve şimdi kullandığınız ||
sonra wait
öylesine değil, iç fonksiyonu, set -e
ikincisi içeride düzgün çalışır:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup
set -e
outer() {
echo '--> outer'
inner &
wait $! || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
İşte bu fikir üzerine inşa edilen genel fonksiyon. Kaldırmak eğer tüm POSIX uyumlu kabuklarda çalışmalıdır local
anahtar kelimeleri yani hepsini yerine, local x=y
sadece birlikte x=y
:
# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
local cmd="$1"; shift
local exit_code=0
local e_was_set=1; if ! is_shell_attribute_set e; then
set -e
e_was_set=0
fi
"$cmd" "$@" &
wait $! || {
exit_code=$?
}
if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
set +e
fi
if [ -n "$CLEANUP" ]; then
RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
return $?
fi
return $exit_code
}
is_shell_attribute_set() { # attribute, like "x"
case "$-" in
*"$1"*) return 0 ;;
*) return 1 ;;
esac
}
Kullanım örneği:
#!/bin/sh
set -e
# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh
main() {
echo "--> main: $@"
CLEANUP=cleanup run inner "$@"
echo "<-- main"
}
inner() {
echo "--> inner: $@"
sleep 0.5; if [ "$1" = 'fail' ]; then
oh_my_god_look_at_this
fi
echo "<-- inner"
}
cleanup() {
echo "--> cleanup: $@"
echo " RUN_CMD = '$RUN_CMD'"
echo " RUN_EXIT_CODE = $RUN_EXIT_CODE"
sleep 0.3
echo '<-- cleanup'
return $RUN_EXIT_CODE
}
main "$@"
Örneği çalıştırma:
$ ./so_3 fail; echo "exit code: $?"
--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
RUN_CMD = 'inner'
RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127
$ ./so_3 pass; echo "exit code: $?"
--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
RUN_CMD = 'inner'
RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0
Bu yöntemi kullanırken bilmeniz gereken tek şey, ilettiğiniz komuttan yapılan Shell değişkenlerinin tüm değişikliklerinin run
çağrı işlevine yayılmamasıdır, çünkü komut bir alt kabukta çalışır.