트랩을 사용하여 모든 오류에 대한 함수를 호출하여 오류보고를 작성하려고합니다.
Trap "_func" ERR
ERR 신호가 전송 된 라인을 얻을 수 있습니까? 껍질은 bash입니다.
그렇게하면 사용 된 명령을 읽고보고하고 일부 작업을 기록 / 수행 할 수 있습니다.
아니면 내가이 모든 잘못 가고 있습니까?
나는 다음과 같이 테스트했다.
#!/bin/bash
trap "ECHO $LINENO" ERR
echo hello | grep "asdf"
그리고 2 $LINENO
를 반환합니다. 작동하지 않습니다.
답변
의견에서 지적했듯이 인용이 잘못되었습니다. $LINENO
트랩 라인을 처음 구문 분석 할 때 확장 되지 않도록 작은 따옴표가 필요합니다 .
이것은 작동합니다 :
#! /bin/bash
err_report() {
echo "Error on line $1"
}
trap 'err_report $LINENO' ERR
echo hello | grep foo # This is line number 9
그것을 실행 :
$ ./test.sh
Error on line 9
답변
bash 내장 ‘caller’를 사용할 수도 있습니다.
#!/bin/bash
err_report() {
echo "errexit on line $(caller)" >&2
}
trap err_report ERR
echo hello | grep foo
파일 이름도 인쇄합니다.
$ ./test.sh
errexit on line 9 ./test.sh
답변
위의 @Mat에서 제공 한 답변을 정말 좋아합니다. 이를 바탕으로 오류에 대한 약간의 컨텍스트를 제공하는 작은 도우미를 작성했습니다.
실패를 일으킨 줄을 스크립트에서 검사 할 수 있습니다.
err() {
echo "Error occurred:"
awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L=$1 $0
}
trap 'err $LINENO' ERR
다음은 작은 테스트 스크립트입니다.
#!/bin/bash
set -e
err() {
echo "Error occurred:"
awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L=$1 $0
}
trap 'err $LINENO' ERR
echo one
echo two
echo three
echo four
false
echo five
echo six
echo seven
echo eight
우리가 그것을 실행할 때 우리는 다음을 얻습니다.
$ /tmp/test.sh
one
two
three
four
Error occurred:
12 echo two
13 echo three
14 echo four
15 >>>false
16 echo five
17 echo six
18 echo seven
답변
다른 답변에서 영감을 얻은 간단한 컨텍스트 오류 처리기가 있습니다.
trap '>&2 echo Command failed: $(tail -n+$LINENO $0 | head -n1)' ERR
필요한 경우 꼬리 및 머리 대신 awk를 사용할 수도 있습니다 .
답변
@sanmai와 @unpythonic에서 영감을 얻은 또 다른 버전이 있습니다. 그것은 오류 주위의 스크립트 줄을 줄 번호와 종료 상태로 표시합니다. 꼬리 및 머리를 사용하면 awk 솔루션보다 간단합니다.
가독성을 위해 여기에 두 줄로 표시-원하는 경우 (을 유지 ;
) 이러한 줄을 하나로 결합 할 수 있습니다 .
trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR
이것은 set -euo pipefail
( 비공식 엄격 모드 ) 와 잘 작동 합니다. 정의되지 않은 변수 오류는 ERR
의사 신호 를 발생시키지 않고 줄 번호를 제공 하지만 다른 경우에는 컨텍스트를 표시합니다.
출력 예 :
myscript.sh: line 27: blah: command not found
Error - exited with status 127 at line 27:
24 # Do something
25 lines=$(wc -l /etc/passwd)
26 # More stuff
27 blah
28
29 # Check time
30 time=$(date)
답변
ERR 신호가 전송 된 라인을 얻을 수 있습니까?
예, LINENO
그리고 BASH_LINENO
변수는 실패의 라인과까지 이어지는 라인을 얻기를위한 저녁 식사 유용합니다.
아니면 내가이 모든 잘못 가고 있습니까?
아니, -q
grep과 함께 옵션이 누락되었습니다 …
echo hello | grep -q "asdf"
…으로 -q
옵션 grep
반환 0
을 위해 true
및 1
위해 false
. 그리고 Bash에서는 trap
그렇지 않습니다 Trap
…
trap "_func" ERR
… 나는 네이티브 솔루션이 필요합니다 …
약간 더 순환적인 복잡성을 가진 것들을 디버깅하는 데 유용한 트래 퍼가 있습니다 …
## Outputs Front-Mater formatted failures for functions not returning 0
## Use the following line after sourcing this file to set failure trap
## trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
failure(){
local -n _lineno="${1:-LINENO}"
local -n _bash_lineno="${2:-BASH_LINENO}"
local _last_command="${3:-${BASH_COMMAND}}"
local _code="${4:-0}"
## Workaround for read EOF combo tripping traps
if ! ((_code)); then
return "${_code}"
fi
local _last_command_height="$(wc -l <<<"${_last_command}")"
local -a _output_array=()
_output_array+=(
'---'
"lines_history: [${_lineno} ${_bash_lineno[*]}]"
"function_trace: [${FUNCNAME[*]}]"
"exit_code: ${_code}"
)
if [[ "${#BASH_SOURCE[@]}" -gt '1' ]]; then
_output_array+=('source_trace:')
for _item in "${BASH_SOURCE[@]}"; do
_output_array+=(" - ${_item}")
done
else
_output_array+=("source_trace: [${BASH_SOURCE[*]}]")
fi
if [[ "${_last_command_height}" -gt '1' ]]; then
_output_array+=(
'last_command: ->'
"${_last_command}"
)
else
_output_array+=("last_command: ${_last_command}")
fi
_output_array+=('---')
printf '%s\n' "${_output_array[@]}" >&2
exit ${_code}
}
… 그리고 함수 추적을 위해 위의 트랩을 설정하는 방법에 미묘한 차이점을 노출시키는 사용 스크립트 예제 …
#!/usr/bin/env bash
set -E -o functrace
## Optional, but recommended to find true directory this script resides in
__SOURCE__="${BASH_SOURCE[0]}"
while [[ -h "${__SOURCE__}" ]]; do
__SOURCE__="$(find "${__SOURCE__}" -type l -ls | sed -n 's@^.* -> \(.*\)@\1@p')"
done
__DIR__="$(cd -P "$(dirname "${__SOURCE__}")" && pwd)"
## Source module code within this script
source "${__DIR__}/modules/trap-failure/failure.sh"
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
something_functional() {
_req_arg_one="${1:?something_functional needs two arguments, missing the first already}"
_opt_arg_one="${2:-SPAM}"
_opt_arg_two="${3:0}"
printf 'something_functional: %s %s %s' "${_req_arg_one}" "${_opt_arg_one}" "${_opt_arg_two}"
## Generate an error by calling nothing
"${__DIR__}/nothing.sh"
}
## Ignoring errors prevents trap from being triggered
something_functional || echo "Ignored something_functional returning $?"
if [[ "$(something_functional 'Spam!?')" == '0' ]]; then
printf 'Nothing somehow was something?!\n' >&2 && exit 1
fi
## And generating an error state will cause the trap to _trace_ it
something_functional '' 'spam' 'Jam'
위의 내용은 Bash 버전 4 이상에서 테스트되었으므로 4 이전 버전에 필요한 것이 있으면 의견을 남기거나 최소 버전이 4 인 시스템에서 장애를 포착하지 못하면 문제 를여십시오.
주요 테이크 아웃 은 …
set -E -o functrace
-
-E
함수 내에서 오류가 발생 합니다 -
-o functrace
함수 내에서 무언가 실패했을 때 더 자세한 정보를 제공합니다.
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
-
작은 따옴표는 함수 호출에 사용되며 큰 따옴표는 개별 인수에 사용됩니다
-
트랩에 링크 된 이후 버전에서는 최종 실패 라인이 출력으로 만들 수 있도록 현재 값 대신 참조
LINENO
및BASH_LINENO
전달됨 -
오류를 반환 한 명령을 얻기 위해 먼저 값
BASH_COMMAND
과 종료 상태 ($?
)가 전달되고, 오류가 아닌 상태에서 트랩이 트리거되지 않도록하는 두 번째 값 이 전달됩니다.
그리고 다른 사람들은 동의하지 않을 수 있지만 출력 배열을 작성하고 각 배열 요소를 자체 줄에 인쇄하기 위해 printf를 사용하는 것이 더 쉽다는 것을 알았습니다 …
printf '%s\n' "${_output_array[@]}" >&2
… >&2
끝 의 비트는 오류를 표준 위치로 이동시키고 오류를 캡처 할 수있게합니다 …
## ... to a file...
some_trapped_script.sh 2>some_trapped_errros.log
## ... or by ignoring standard out...
some_trapped_script.sh 1>/dev/null
Stack Overflow에서 이러한 예제 와 다른 예제 에서 볼 수 있듯이 내장 유틸리티를 사용하여 디버깅 보조 도구를 작성하는 방법에는 여러 가지가 있습니다.