如何找到 Bash 中发生错误的行号?
例子
我创建了以下带有行号的简单脚本来解释我们需要什么。该脚本将从以下位置复制文件
cp $file1 $file2
cp $file3 $file4
当其中一个cp
命令失败时,该函数将退出1号出口。我们希望向该函数添加打印错误及行号(例如 8 或 12)的功能。
这可能吗?
示例脚本
1 #!/bin/bash
2
3
4 function in_case_fail {
5 [[ $1 -ne 0 ]] && echo "fail on $2" && exit 1
6 }
7
8 cp $file1 $file2
9 in_case_fail $? "cp $file1 $file2"
10
11
12 cp $file3 $file4
13 in_case_fail $? "cp $file3 $file4"
14
答案1
我不会使用你的函数,而是使用这个方法:
$ cat yael.bash
#!/bin/bash
set -eE -o functrace
file1=f1
file2=f2
file3=f3
file4=f4
failure() {
local lineno=$1
local msg=$2
echo "Failed at $lineno: $msg"
}
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
cp -- "$file1" "$file2"
cp -- "$file3" "$file4"
这是通过捕获 ERR 然后failure()
使用当前行号 + 执行的 bash 命令调用该函数来实现的。
例子
在这里,我没有注意创建文件f1
、f2
、f3
或f4
。当我运行上面的脚本时:
$ ./yael.bash
cp: cannot stat ‘f1’: No such file or directory
Failed at 17: cp -- "$file1" "$file2"
它失败,报告行号加上已执行的命令。
答案2
除了LINENO
包含当前行号之外,还有BASH_LINENO
and FUNCNAME
(and BASH_SOURCE
) 数组包含函数名称和调用它们的行号。
所以你可以这样做:
#!/bin/bash
error() {
printf "'%s' failed with exit code %d in function '%s' at line %d.\n" "${1-something}" "$?" "${FUNCNAME[1]}" "${BASH_LINENO[0]}"
}
foo() {
( exit 0 ) || error "this thing"
( exit 123 ) || error "that thing"
}
foo
运行会打印
'that thing' failed with exit code 123 in function 'foo' at line 9.
如果您使用set -e
, 或trap ... ERR
来自动检测错误,请注意它们有一些警告。包含脚本当时正在执行的操作的描述也更困难(就像您在示例中所做的那样),尽管这对于普通用户来说可能比行号更有用。
例如,请参阅以下问题set -e
以及其他问题:
答案3
Bash 有一个内置变量$LINENO
,在语句中时它会被当前行号替换,所以你可以这样做
in_case_fail $? "at $LINENO: cp $file1 $file2"
您还可以尝试使用trap ... ERR
命令失败时运行的命令(如果结果未经测试)。例如:
trap 'rc=$?; echo "error code $rc at $LINENO"; exit $rc' ERR
然后,如果类似的命令cp $file1 $file2
失败,您将收到带有行号和退出的错误消息。您还会发现变量中的命令有错误$BASH_COMMAND
(尽管没有任何重定向等)。
答案4
我决定使用以下内容,其中包括堆栈跟踪,就好像list of fns [list of line numbers]
脚本位于函数中一样,否则包含脚本行号。还要感谢之前的答案,我在经过一些实验来处理基于 fn 和非 fn 的脚本后对其进行了扩展。
set -eE -o functrace
failure() {
local lineno=$2
local fn=$3
local exitstatus=$4
local msg=$5
local lineno_fns=${1% 0}
if [[ "$lineno_fns" != "0" ]] ; then
lineno="${lineno} ${lineno_fns}"
fi
echo "${BASH_SOURCE[1]}:${fn}[${lineno}] Failed with status ${exitstatus}: $msg"
}
trap 'failure "${BASH_LINENO[*]}" "$LINENO" "${FUNCNAME[*]:-script}" "$?" "$BASH_COMMAND"' ERR
所以一个简单的命令失败看起来像:
/tmp/b:script[32] Failed with status 1: cp fred john
和嵌套函数(其中 hello1 调用 hello2):
/tmp/b:hello2 hello1 main[24 19 29] Failed with status 1: cp john 22
我偶尔会报告退出状态,它会为您提供额外的信息,并且与其他人不同的是,我想要脚本的完整路径名。
需要进一步的工作来报告因信号而退出的情况。