‘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 != 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
당신이 때 어떻게 검사 할 필요가 리턴 코드는 산술 컨텍스트 대신를 사용합니다 [ ... -ne
:
ret=$?
# do something
if (( ret != 0 )); 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"