トラップ、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ます0true1そしてfalse。バッシュでは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および終了ステータス($?)が渡されます。最初にエラーを返すコマンドを取得し、2番目にエラーではなくトラップがトリガーされないようにするために渡されます。

他の人は同意しないかもしれませんが、出力配列を作成し、printfを使用して各配列要素を1行に印刷する方が簡単です。

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を使用します。

読みやすくするために2行で表示されます。必要に応じて、2行を1つにマージできます(メンテナンス;)。

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)

関連情報