트랩, ERR 및 에코 오류 라인

트랩, ERR 및 에코 오류 라인

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

Trap "_func" ERR

ERR 신호가 전송되는 라인을 얻을 수 있습니까? 쉘은 bash입니다.

이렇게 하면 어떤 명령이 사용되었는지 읽고 보고할 수 있으며 일부 작업을 기록/수행할 수 있습니다.

아니면 내가 다 잘못하고 있는 걸까?

나는 다음을 테스트했습니다.

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

echo hello | grep "asdf"

2를 반환 하고 $LINENO있습니다. 작동 안함.

답변1

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

답변2

bash 내장 "발신자"를 사용할 수도 있습니다.

#!/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

답변3

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

예, LINENO변수는 BASH_LINENO실패한 행과 실패를 일으킨 행을 가져오는 데 유용합니다.

아니면 내가 다 잘못하고 있는 걸까?

아니요, 그냥 빠졌어요-q그렙 옵션...

echo hello | grep -q "asdf"

... 그리고-q옵션이 반환 grep됩니다0true그리고1false. 배쉬에서는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
  • 함수 호출에는 작은따옴표가 사용되고 개별 매개변수에는 큰따옴표가 사용됩니다.

  • 참고자료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

이것들과다른 예스택 오버플로에는 내장 유틸리티를 사용하여 디버깅 지원을 구축하는 방법이 많이 있습니다.

답변4

@sanmai 및 @unpythonic에서 영감을 받은 또 다른 버전이 있습니다. 오류, 줄 번호 및 종료 상태 주변의 스크립트 줄을 표시합니다. 이는 awk 솔루션보다 간단해 보이기 때문에 tail 및 head를 사용합니다.

가독성을 위해 두 줄로 표시됩니다. 원하는 경우 두 줄을 하나로 병합할 수 있습니다(유지 ;).

trap 'echo >&2 "Error - exited with status $? at line $LINENO:"; 
         pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7 >&2' ERR

이는 set -eEuo 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)

관련 정보