How to get the real line number of a failed Bash command?

In the process of finding bugs in my Bash scripts, I experimented with "set -e", "set -E" and the "trap" command. In this process, I discovered strange behavior in how it $LINENOis evaluated in the context of functions. Firstly, here is a stripped down version of how I am trying to log errors:

#!/bin/bash

set -E
trap 'echo Failed on line: $LINENO at command: $BASH_COMMAND && exit $?' ERR

Now the behavior is different from where the failure occurs. For example, if I follow the above:

echo "Should fail at: $((LINENO + 1))"
false

I get the following output:

Should fail at: 6
Failed on line: 6 at command: false

Everything is as expected. Line 6 is a line containing a single "false" command. But if I end my unsuccessful command in a function and call it like this:

function failure {
    echo "Should fail at $((LINENO + 1))"
    false
}
failure

Then I get the following output:

Should fail at 7
Failed on line: 5 at command: false

, $BASH_COMMAND : "false", $LINENO "" . . , $BASH_COMMAND?

, Bash. 3.2.51. , , , 3.2.51.

EDIT: , , . , , .

script:

#!/bin/bash

set -E
function handle_error {
    local retval=$?
    local line=$1
    echo "Failed at $line: $BASH_COMMAND"
    exit $retval
}
trap 'handle_error $LINENO' ERR

function fail {
    echo "I expect the next line to be the failing line: $((LINENO + 1))"
    command_that_fails
}

fail

:

I expect the next line to be the failing line: 14
Failed at 14: command_that_fails

:

I expect the next line to be the failing line: 14
Failed at 12: command_that_fails

12 command_that_fails. 12 function fail {, . ${BASH_LINENO[@]}, 14.

+4
3

bash 4.1 , , , .

#!/bin/bash

set -E
set -o functrace
function handle_error {
    local retval=$?
    local line=${last_lineno:-$1}
    echo "Failed at $line: $BASH_COMMAND"
    echo "Trace: " "$@"
    exit $retval
}
if (( ${BASH_VERSION%%.*} <= 3 )) || [[ ${BASH_VERSION%.*} = 4.0 ]]; then
        trap '[[ $FUNCNAME = handle_error ]] || { last_lineno=$real_lineno; real_lineno=$LINENO; }' DEBUG
fi
trap 'handle_error $LINENO ${BASH_LINENO[@]}' ERR

fail() {
    echo "I expect the next line to be the failing line: $((LINENO + 1))"
    command_that_fails
}

fail
+3

BASH_LINENO - . : ${BASH_LINENO[1]}, ${BASH_LINENO[2]} .. . ( BASH_SOURCE, ).

, , :

failure() {
  local lineno=$1
  echo "Failed at $lineno"
}
trap 'failure ${LINENO}' ERR

fooobar.com/questions/35586/... ( ).

0

This behavior is very reasonable.

The whole picture of the call stack provides comprehensive information whenever an error occurs. Your example showed a good error message; you could see where the error really occurred, and which line called the function, etc.

If the interpreter / compiler cannot specify exactly where the error actually occurs, you can more easily confuse.

-1
source

Source: https://habr.com/ru/post/1545923/


All Articles