여러 명령의 Bash 종료 상태를 효율적으로 확인
'try'문과 같지만 bash 내에 여러 명령에 대해 pipefail과 비슷한 것이 있습니까? 나는 이런 식으로하고 싶다 :
echo "trying stuff"
try {
command1
command2
command3
}
그리고 언제라도 명령이 실패하면 해당 명령의 오류를 제거하고 반향하십시오. 나는 다음과 같은 일을하고 싶지 않습니다.
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
그리고 등등 ... 또는 같은 것 :
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
내가 믿는 (각각 틀렸다면 정정) 각 명령의 주장이 서로 방해하기 때문에. 이 두 가지 방법은 끔찍하게 길고 불쾌한 것처럼 보이므로 더 효율적인 방법을 원합니다.
명령을 시작하고 테스트하는 기능을 작성할 수 있습니다. 명령으로 설정된 환경 변수를 가정 command1
하고 가정하십시오 command2
.
function mytest {
"$@"
local status=$?
if [ $status -ne 0 ]; then
echo "error with $1" >&2
fi
return $status
}
mytest $command1
mytest $command2
"오류를 제거하고 반향"한다는 것은 무엇을 의미합니까? 명령이 실패하자마자 스크립트를 종료하려면 다음을 수행하십시오.
set -e # DON'T do this. See commentary below.
스크립트 시작시 (아래 경고에 유의) 오류 메시지를 에코하지 마십시오. 실패한 명령이 처리하도록하십시오. 다시 말해, 다음과 같은 경우 :
#!/bin/sh
set -e # Use caution. eg, don't do this
command1
command2
command3
stderr에 오류 메시지를 인쇄하는 동안 command2가 실패하면 원하는 것을 달성 한 것 같습니다. (내가 원하는 것을 잘못 해석하지 않는 한!)
결과적으로 사용자가 작성하는 모든 명령은 올바르게 작동해야합니다. stdout 대신 stderr에 오류를보고해야하며 (질문의 샘플 코드는 stdout에 오류를 인쇄 함) 실패하면 0이 아닌 상태로 종료해야합니다.
그러나 나는 더 이상 이것을 좋은 습관으로 생각하지 않습니다. set -e
다른 버전의 bash로 의미를 변경했으며 간단한 스크립트에서는 잘 작동하지만 본질적으로 사용할 수없는 많은 경우가 있습니다. ( set -e; foo() { false; echo should not print; } ; foo && echo ok
여기서 의미를 고려하십시오 : 여기서 의미는 다소 합리적이지만, 조기 종료 옵션 설정에 의존하는 함수로 코드를 리팩터링하면 쉽게 물릴 수 있습니다.) IMO 작성하는 것이 좋습니다 :
#!/bin/sh
command1 || exit
command2 || exit
command3 || exit
또는
#!/bin/sh
command1 && command2 && command3
Red Hat 시스템에서 광범위하게 사용하는 일련의 스크립팅 기능이 있습니다. 시스템 기능을 사용하여 /etc/init.d/functions
녹색 [ OK ]
및 빨간색 [FAILED]
상태 표시기 를 인쇄 합니다.
$LOG_STEPS
실패한 명령을 기록하려는 경우 선택적으로 변수를 로그 파일 이름 으로 설정할 수 있습니다 .
용법
step "Installing XFS filesystem tools:"
try rpm -i xfsprogs-*.rpm
next
step "Configuring udev:"
try cp *.rules /etc/udev/rules.d
try udevtrigger
next
step "Adding rc.postsysinit hook:"
try cp rc.postsysinit /etc/rc.d/
try ln -s rc.d/rc.postsysinit /etc/rc.postsysinit
try echo $'\nexec /etc/rc.postsysinit' >> /etc/rc.sysinit
next
산출
Installing XFS filesystem tools: [ OK ]
Configuring udev: [FAILED]
Adding rc.postsysinit hook: [ OK ]
암호
#!/bin/bash
. /etc/init.d/functions
# Use step(), try(), and next() to perform a series of commands and print
# [ OK ] or [FAILED] at the end. The step as a whole fails if any individual
# command fails.
#
# Example:
# step "Remounting / and /boot as read-write:"
# try mount -o remount,rw /
# try mount -o remount,rw /boot
# next
step() {
echo -n "$@"
STEP_OK=0
[[ -w /tmp ]] && echo $STEP_OK > /tmp/step.$$
}
try() {
# Check for `-b' argument to run command in the background.
local BG=
[[ $1 == -b ]] && { BG=1; shift; }
[[ $1 == -- ]] && { shift; }
# Run the command.
if [[ -z $BG ]]; then
"$@"
else
"$@" &
fi
# Check if command failed and update $STEP_OK if so.
local EXIT_CODE=$?
if [[ $EXIT_CODE -ne 0 ]]; then
STEP_OK=$EXIT_CODE
[[ -w /tmp ]] && echo $STEP_OK > /tmp/step.$$
if [[ -n $LOG_STEPS ]]; then
local FILE=$(readlink -m "${BASH_SOURCE[1]}")
local LINE=${BASH_LINENO[0]}
echo "$FILE: line $LINE: Command \`$*' failed with exit code $EXIT_CODE." >> "$LOG_STEPS"
fi
fi
return $EXIT_CODE
}
next() {
[[ -f /tmp/step.$$ ]] && { STEP_OK=$(< /tmp/step.$$); rm -f /tmp/step.$$; }
[[ $STEP_OK -eq 0 ]] && echo_success || echo_failure
echo
return $STEP_OK
}
가치있는 것을 위해, 각 명령의 성공 여부를 확인하는 코드를 작성하는 짧은 방법은 다음과 같습니다.
command1 || echo "command1 borked it"
command2 || echo "command2 borked it"
여전히 지루하지만 적어도 읽을 수 있습니다.
대안은 단순히 명령을 함께 결합하여 &&
실패한 첫 번째 명령 이 나머지를 실행하지 못하게하는 것입니다.
command1 &&
command2 &&
command3
이것은 질문에서 요청한 구문이 아니지만 설명하는 사용 사례의 일반적인 패턴입니다. 일반적으로 명령은 인쇄 실패에 대한 책임을 져야하므로 수동으로 -q
할 필요가 없습니다 (원하는 경우 오류를 끄는 플래그가있을 수 있음). 이러한 명령을 수정하는 기능이 있다면 다른 명령으로 감싸기보다는 실패시 소리를 내도록 편집합니다.
또한 다음을 수행 할 필요가 없습니다.
command1
if [ $? -ne 0 ]; then
간단히 말할 수 있습니다.
if ! command1; then
러너 함수를 작성하거나을 set -e
사용하는 대신 다음을 사용하십시오 trap
.
trap 'echo "error"; do_cleanup failed; exit' ERR
trap 'echo "received signal to stop"; do_cleanup interrupted; exit' SIGQUIT SIGTERM SIGINT
do_cleanup () { rm tempfile; echo "$1 $(date)" >> script_log; }
command1
command2
command3
트랩은 심지어 행 번호와이를 트리거 한 명령의 명령 행에 액세스 할 수 있습니다. 변수는 $BASH_LINENO
및 $BASH_COMMAND
입니다.
개인적으로 나는 여기 에서 볼 수 있듯이 가벼운 접근법을 선호합니다 .
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
asuser() { sudo su - "$1" -c "${*:2}"; }
사용법 예 :
try apt-fast upgrade -y
try asuser vagrant "echo 'uname -a' >> ~/.profile"
run() {
$*
if [ $? -ne 0 ]
then
echo "$* failed with exit code $?"
return 1
else
return 0
fi
}
run command1 && run command2 && run command3
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__!"
}
코드는 내 bash 상용구 / 프레임 워크 의 일부입니다 . 또한 역 추적 및 예외를 포함한 오류 처리 (일부 다른 멋진 기능)와 같은 것으로 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 에 있습니다 .
첫 번째 답변에 대한 의견을 제시 할 수 없어서 죄송합니다. cmd_output = $ ($ @) 명령을 실행하려면 새 인스턴스를 사용해야합니다.
#!/bin/bash
function check_exit {
cmd_output=$($@)
local status=$?
echo $status
if [ $status -ne 0 ]; then
echo "error with $1" >&2
fi
return $status
}
function run_command() {
exit 1
}
check_exit run_command
들어 물고기 쉘 이 스레드에서 우연히 발견 사용자.
하자 foo
없다 "반환"(에코) 값을 수행하는 함수가 될 수 있지만, 평소와 같이 종료 코드를 설정합니다. 함수를 호출 한 후
확인하지 않으려면 $status
다음을 수행하십시오.
foo; and echo success; or echo failure
한 줄에 맞추기가 너무 길면 :
foo; and begin
echo success
end; or begin
echo failure
end
사용할 때 ssh
연결 문제로 인한 문제와 errexit
( set -e
) 모드 에서 원격 명령의 오류 코드를 구별해야 합니다. 다음 기능을 사용합니다.
# prepare environment on calling site:
rssh="ssh -o ConnectionTimeout=5 -l root $remote_ip"
function exit255 {
local flags=$-
set +e
"$@"
local status=$?
set -$flags
if [[ $status == 255 ]]
then
exit 255
else
return $status
fi
}
export -f exit255
# callee:
set -e
set -o pipefail
[[ $rssh ]]
[[ $remote_ip ]]
[[ $( type -t exit255 ) == "function" ]]
rjournaldir="/var/log/journal"
if exit255 $rssh "[[ ! -d '$rjournaldir/' ]]"
then
$rssh "mkdir '$rjournaldir/'"
fi
rconf="/etc/systemd/journald.conf"
if [[ $( $rssh "grep '#Storage=auto' '$rconf'" ) ]]
then
$rssh "sed -i 's/#Storage=auto/Storage=persistent/' '$rconf'"
fi
$rssh systemctl reenable systemd-journald.service
$rssh systemctl is-enabled systemd-journald.service
$rssh systemctl restart systemd-journald.service
sleep 1
$rssh systemctl status systemd-journald.service
$rssh systemctl is-active systemd-journald.service
RedHat 이외의 시스템에서 위 의 @ john-kugelman의 멋진 솔루션 을 다음 코드에서 주석 처리하여 사용할 수 있습니다 .
. /etc/init.d/functions
그런 다음 아래 코드를 끝에 붙여 넣으십시오. 전체 공개 : 이것은 Centos 7에서 가져온 위에서 언급 한 파일의 관련 비트를 직접 복사하여 붙여 넣는 것입니다.
MacOS 및 Ubuntu 18.04에서 테스트되었습니다.
BOOTUP=color
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \\033[0;39m"
echo_success() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
echo -n $" OK "
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 0
}
echo_failure() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
echo -n $"FAILED"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
echo_passed() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo -n $"PASSED"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
echo_warning() {
[ "$BOOTUP" = "color" ] && $MOVE_TO_COL
echo -n "["
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo -n $"WARNING"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo -n "]"
echo -ne "\r"
return 1
}
기능적인 방식으로 상태 확인
assert_exit_status() {
lambda() {
local val_fd=$(echo $@ | tr -d ' ' | cut -d':' -f2)
local arg=$1
shift
shift
local cmd=$(echo $@ | xargs -E ':')
local val=$(cat $val_fd)
eval $arg=$val
eval $cmd
}
local lambda=$1
shift
eval $@
local ret=$?
$lambda : <(echo $ret)
}
용법:
assert_exit_status 'lambda status -> [[ $status -ne 0 ]] && echo Status is $status.' lls
산출
Status is 127
참고 URL : https://stackoverflow.com/questions/5195607/checking-bash-exit-status-of-several-commands- 효율적으로
'Programing' 카테고리의 다른 글
C ++에서 문자열이 다른 문자열로 끝나는 지 확인 (0) | 2020.04.05 |
---|---|
터미널을 통해 JavaScript 스크립트를 어떻게 실행합니까? (0) | 2020.04.05 |
JavaScript 또는 jQuery를 사용하여 새 탭에서 URL을 여는 방법은 무엇입니까? (0) | 2020.04.05 |
장고 날짜 시간 문제 (default = datetime.now ()) (0) | 2020.04.05 |
HTML의 src 속성이 (0) | 2020.04.05 |