트랩, ERR 및 오류 라인 에코


30

트랩을 사용하여 모든 오류에 대한 함수를 호출하여 오류보고를 작성하려고합니다.

Trap "_func" ERR

ERR 신호가 전송 된 라인을 얻을 수 있습니까? 껍질은 bash입니다.

그렇게하면 사용 된 명령을 읽고보고하고 일부 작업을 기록 / 수행 할 수 있습니다.

아니면 내가이 모든 잘못 가고 있습니까?

나는 다음과 같이 테스트했다.

#!/bin/bash
trap "ECHO $LINENO" ERR

echo hello | grep "asdf"

그리고 2 $LINENO를 반환합니다. 작동하지 않습니다.


bash 디버거 스크립트를 볼 수 있습니다 bashdb. 첫 번째 인수 trap는 원하는 컨텍스트에서 평가되는 변수를 포함 할 수 있습니다. 그래서 trap 'echo $LINENO' ERR'작동합니다.
donothingsuccessfully

흠 방금 나쁜 에코로 이것을 시도 | grep 명령을 실행하면 Trap 문의 줄이 반환됩니다. 그러나 나는 bashdb를 살펴볼 것이다
Mechaflash

죄송합니다. 원래 질문에 기본 솔루션이 필요하다고 명시하지 않았습니다. 질문을 편집했습니다.
Mechaflash

죄송합니다 trap 'echo $LINENO' ERR. 예제 줄을 정리했습니다 : . 첫 번째 인수 trap는 전체 echo $LINENO인용 부호입니다. 이것은 bash에 있습니다.
donothingsuccessfully

5
@Mechaflash trap 'echo $LINENO' ERR큰 따옴표가 아닌 작은 따옴표로 묶어야합니다. 작성한 명령으로 $LINENO2 행이 구문 분석 될 때 확장되어 트랩이 출력됩니다 echo 2(또는 오히려 ECHO 2출력됩니다 bash: ECHO: command not found).
Gilles 'SO- 악한 중지'

답변:


61

의견에서 지적했듯이 인용이 잘못되었습니다. $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

함수 호출 예제를 주셔서 감사합니다. 이 경우 큰 따옴표가 변수를 확장했다는 것을 몰랐습니다.
Mechaflash

echo hello | grep foo나를 위해 오류를 던지지 않는 것 같습니다. 내가 뭔가를 오해하고 있습니까?
지리학

@geotheory 내 시스템 grep에서 일치하는 경우 종료 상태가 0이고, 일치하지 않은 경우 1이고 오류의 경우> 1입니다. 다음을 사용하여 시스템의 동작을 확인할 수 있습니다.echo hello | grep foo; echo $?
Patrick

아니 당신이 맞아 그건 오류입니다 :)
geotheory

명령 행에서 오류를 발생시키기 위해 호출 행에서 -e를 사용할 필요가 없습니까? #! / bin / bash -e?
Tim Bird

14

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

6

위의 @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

$(caller)실패가 현재 스크립트에 있지 않지만 가져 오기 중 하나 인 경우에도 컨텍스트를 제공하기 위해의 데이터를 사용하는 것이 더 좋습니다 . 그래도 아주 좋은!
tricasse

2

다른 답변에서 영감을 얻은 간단한 컨텍스트 오류 처리기가 있습니다.

trap '>&2 echo Command failed: $(tail -n+$LINENO $0 | head -n1)' ERR

필요한 경우 꼬리 및 머리 대신 awk를 사용할 수도 있습니다 .


1
다른 대답이 위의 3 줄과 3 줄 아래에 컨텍스트를 제공하는 이유가 있습니다. 오류가 연속 줄에서 발생하면 어떻게됩니까?
iruvar

@iruvar 이것은 이해되지만 추가 컨텍스트가 필요하지 않습니다. 한 줄의 문맥은 얻는 것만 큼 간단하고 내가 필요한만큼 충분합니다
sanmai

내 친구 확인, +1
iruvar

0

@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)

0

ERR 신호가 전송 된 라인을 얻을 수 있습니까?

예, LINENO그리고 BASH_LINENO변수는 실패의 라인과까지 이어지는 라인을 얻기를위한 저녁 식사 유용합니다.

아니면 내가이 모든 잘못 가고 있습니까?

아니, -qgrep과 함께 옵션이 누락되었습니다 ...

echo hello | grep -q "asdf"

...으로 -q옵션 grep반환 0을 위해 true1위해 false. 그리고 Bash에서는 trap그렇지 않습니다 Trap...

trap "_func" ERR

... 나는 네이티브 솔루션이 필요합니다 ...

약간 더 순환적인 복잡성을 가진 것들을 디버깅하는 데 유용한 트래 퍼가 있습니다 ...

failure.sh

## 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}
}

... 그리고 함수 추적을 위해 위의 트랩을 설정하는 방법에 미묘한 차이점을 노출시키는 사용 스크립트 예제 ...

example_usage.sh

#!/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
  • 작은 따옴표는 함수 호출에 사용되며 큰 따옴표는 개별 인수에 사용됩니다

  • 트랩에 링크 된 이후 버전에서는 최종 실패 라인이 출력으로 만들 수 있도록 현재 값 대신 참조 LINENOBASH_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에서 이러한 예제다른 예제 에서 볼 수 있듯이 내장 유틸리티를 사용하여 디버깅 보조 도구를 작성하는 방법에는 여러 가지가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.