쉘 스크립트를 작성 중이며 터미널 앱이 설치되어 있는지 확인해야합니다. 더 깔끔한 방법이 없다면 TRY / CATCH 명령을 사용 하여이 작업을 수행하고 싶습니다.
답변
Bash에 TRY CATCH 명령이 있습니까?
아니.
배쉬에는 많은 프로그래밍 언어에서 찾을 수있는만큼 사치가 없습니다.
try/catch
bash 에는 없습니다 . 그러나, 하나는 사용하여 비슷한 동작을 달성 할 수있다 &&
나 ||
.
사용 ||
:
경우 command1
실패 후 command2
다음과 같이 실행
command1 || command2
마찬가지로, 사용 &&
, command2
경우에 실행 command1
성공
가장 가까운 근사값은 try/catch
다음과 같습니다.
{ # try
command1 &&
#save your output
} || { # catch
# save log for exception
}
또한 bash에는 몇 가지 오류 처리 메커니즘이 포함되어 있습니다.
set -e
간단한 명령이 실패하면 스크립트가 중지됩니다.
또한 왜 안되나요 if...else
? 가장 친한 친구입니다.
답변
여기에서 찾은 몇 가지 답변을 바탕으로 내 프로젝트의 소스로 사용할 작은 도우미 파일을 만들었습니다.
trycatch.sh
#!/bin/bash
function try()
{
[[ $- = *e* ]]; SAVED_OPT_E=$?
set +e
}
function throw()
{
exit $1
}
function catch()
{
export ex_code=$?
(( $SAVED_OPT_E )) && set +e
return $ex_code
}
function throwErrors()
{
set -e
}
function ignoreErrors()
{
set +e
}
다음은 사용중인 모습의 예입니다.
#!/bin/bash
export AnException=100
export AnotherException=101
# start with a try
try
( # open a subshell !!!
echo "do something"
[ someErrorCondition ] && throw $AnException
echo "do something more"
executeCommandThatMightFail || throw $AnotherException
throwErrors # automaticatly end the try block, if command-result is non-null
echo "now on to something completely different"
executeCommandThatMightFail
echo "it's a wonder we came so far"
executeCommandThatFailsForSure || true # ignore a single failing command
ignoreErrors # ignore failures of commands until further notice
executeCommand1ThatFailsForSure
local result = $(executeCommand2ThatFailsForSure)
[ result != "expected error" ] && throw $AnException # ok, if it's not an expected error, we want to bail out!
executeCommand3ThatFailsForSure
echo "finished"
)
# directly after closing the subshell you need to connect a group to the catch using ||
catch || {
# now you can handle
case $ex_code in
$AnException)
echo "AnException was thrown"
;;
$AnotherException)
echo "AnotherException was thrown"
;;
*)
echo "An unexpected exception was thrown"
throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught
;;
esac
}
답변
bash에서 거의 완벽한 try & catch 구현을 개발하여 다음과 같은 코드를 작성할 수 있습니다.
try
echo 'Hello'
false
echo 'This will not be displayed'
catch
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
try-catch 블록을 자체 안에 중첩시킬 수도 있습니다!
try {
echo 'Hello'
try {
echo 'Nested Hello'
false
echo 'This will not execute'
} catch {
echo "Nested Caught (@ $__EXCEPTION_LINE__)"
}
false
echo 'This will not execute too'
} catch {
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
}
코드는 내 배쉬 보일러 플레이트 / 프레임 워크 의 일부입니다 . 또한 역 추적 및 예외를 포함한 오류 처리 (및 기타 유용한 기능)를 사용하여 try & catch 아이디어를 확장합니다.
try & catch를 담당하는 코드는 다음과 같습니다.
set -o pipefail
shopt -s expand_aliases
declare -ig __oo__insideTryCatch=0
# if try-catch is nested, then set +e before so the parent handler doesn't catch us
alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e;
__oo__insideTryCatch+=1; ( set -e;
trap \"Exception.Capture \${LINENO}; \" ERR;"
alias catch=" ); Exception.Extract \$? || "
Exception.Capture() {
local script="${BASH_SOURCE[1]#./}"
if [[ ! -f /tmp/stored_exception_source ]]; then
echo "$script" > /tmp/stored_exception_source
fi
if [[ ! -f /tmp/stored_exception_line ]]; then
echo "$1" > /tmp/stored_exception_line
fi
return 0
}
Exception.Extract() {
if [[ $__oo__insideTryCatch -gt 1 ]]
then
set -e
fi
__oo__insideTryCatch+=-1
__EXCEPTION_CATCH__=( $(Exception.GetLastException) )
local retVal=$1
if [[ $retVal -gt 0 ]]
then
# BACKWARDS COMPATIBILE WAY:
# export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}"
# export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}"
export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}"
export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}"
export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}"
return 1 # so that we may continue with a "catch"
fi
}
Exception.GetLastException() {
if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]]
then
cat /tmp/stored_exception
cat /tmp/stored_exception_line
cat /tmp/stored_exception_source
else
echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}"
fi
rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source
return 0
}
자유롭게 사용하고 포크하고 기여하십시오 -GitHub 에 있습니다 .
답변
당신은 사용할 수 있습니다 trap
:
try { block A } catch { block B } finally { block C }
다음과 같이 번역됩니다.
(
set -Ee
function _catch {
block B
exit 0 # optional; use if you don't want to propagate (rethrow) error to outer shell
}
function _finally {
block C
}
trap _catch ERR
trap _finally EXIT
block A
)
답변
아마도 비슷한 많은 솔루션이 있습니다. 아래는 주석에 설명과 함께 시도 / 캐치를 수행하는 간단하고 효과적인 방법입니다.
#!/bin/bash
function a() {
# do some stuff here
}
function b() {
# do more stuff here
}
# this subshell is a scope of try
# try
(
# this flag will make to exit from current subshell on any error
# inside it (all functions run inside will also break on any error)
set -e
a
b
# do more stuff here
)
# and here we catch errors
# catch
errorCode=$?
if [ $errorCode -ne 0 ]; then
echo "We have an error"
# We exit the all script with the same error, if you don't want to
# exit it and continue, just delete this line.
exit $errorCode
fi
답변
bash
( -e
플래그 를 설정하지 않는 한) 오류 상태를 감지 한 경우 실행중인 실행을 중단하지 않습니다 . 이러한 특수 상황 ( “일반적으로”예외 “)으로 인해”베일 아웃 ” try/catch
을 방지 하기 위해 이를 제공하는 프로그래밍 언어가이를 수행합니다 .
에서 bash
, 대신, 문제의 유일한 명령은 오류 상태를 나타내는, 0보다 종료 코드의 큰 함께 종료됩니다. 당신은 물론 그 확인할 수 있지만 자동 없기 때문에 구제 아무것도를하는 시도 / 캐치는 이해가되지 않습니다. 그 맥락이 부족합니다.
그러나 결정한 시점에서 종료 될 수있는 하위 쉘을 사용하여 제거 를 시뮬레이션 할 수 있습니다.
(
echo "Do one thing"
echo "Do another thing"
if some_condition
then
exit 3 # <-- this is our simulated bailing out
fi
echo "Do yet another thing"
echo "And do a last thing"
) # <-- here we arrive after the simulated bailing out, and $? will be 3 (exit code)
if [ $? = 3 ]
then
echo "Bail out detected"
fi
대신의 some_condition
와 함께 if
당신은 또한 단지 명령을 시도 할 수 있고, 경우에 그것은 실패 (0이 아닌 종료 코드의 이상을 가지고), 구제 :
(
echo "Do one thing"
echo "Do another thing"
some_command || exit 3
echo "Do yet another thing"
echo "And do a last thing"
)
...
불행히도이 기술을 사용하면 255 개의 다른 종료 코드 (1..255)로 제한되며 적절한 예외 개체를 사용할 수 없습니다.
시뮬레이션 된 예외와 함께 전달하기 위해 더 많은 정보가 필요한 경우 서브 쉘의 stdout을 사용할 수 있지만 약간 복잡하고 또 다른 질문입니다 😉
위에서 언급 한 -e
플래그를 쉘에 사용하면 명시 적 exit
진술을 제거 할 수도 있습니다 .
(
set -e
echo "Do one thing"
echo "Do another thing"
some_command
echo "Do yet another thing"
echo "And do a last thing"
)
...
답변
모두가 말했듯이 bash는 언어 지원 try / catch 구문이 없습니다. 명령에 종료 코드가 0이 아닌 경우 -e
인수 와 함께 bash를 시작 하거나 set -e
스크립트 내에서 사용 하여 전체 bash 프로세스를 중단 할 수 있습니다. ( set +e
실패한 명령을 일시적으로 허용 할 수도 있습니다 .)
따라서 try / catch 블록을 시뮬레이트하는 한 가지 기술은 하위 프로세스를 시작하여 -e
활성화 된 작업을 수행하는 것입니다 . 그런 다음 기본 프로세스에서 하위 프로세스의 리턴 코드를 확인하십시오.
Bash는 heredoc 문자열을 지원하므로이를 처리하기 위해 두 개의 별도 파일을 작성할 필요가 없습니다. 아래 예제에서 TRY heredoc은 -e
활성화 된 별도의 bash 인스턴스에서 실행 되므로 명령이 0이 아닌 종료 코드를 반환하면 하위 프로세스가 중단됩니다. 그런 다음 주 프로세스로 돌아가서 catch 블록을 처리하기 위해 리턴 코드를 확인할 수 있습니다.
#!/bin/bash
set +e
bash -e <<TRY
echo hello
cd /does/not/exist
echo world
TRY
if [ $? -ne 0 ]; then
echo caught exception
fi
올바른 언어 지원 try / catch 블록은 아니지만 비슷한 가려움증이 생길 수 있습니다.
