有没有办法调试bash脚本?例如,打印一种执行日志,如“呼叫线路1”,“呼叫线路2”等。
答案 0 :(得分:184)
sh -x script [arg1 ...]
bash -x script [arg1 ...]
这些可以让您了解正在执行的内容。 (另请参阅答案底部附近的“澄清”。)
有时,您需要控制脚本中的调试。在这种情况下,作为Cheeto reminded我,您可以使用:
set -x
这会打开调试。然后,您可以使用以下命令将其关闭:
set +x
(您可以通过分析$-
的当前标志x
找出当前的跟踪状态。)
此外,shell通常为“无执行”提供选项“-n
”,为“详细”模式提供“-v
”选项;您可以结合使用这些来查看shell是否认为它可以执行您的脚本 - 如果您在某处有不平衡的报价,偶尔会有用。
有人认为Bash中的'-x
'选项与其他shell不同(请参阅注释)。 Bash Manual说:
-x
在扩展后,打印一系列简单命令,for
命令,case
命令,select
命令和算术for
命令及其参数或关联的单词列表在它们被执行之前。扩展PS4
变量的值,并在命令及其扩展参数之前打印结果值。
这似乎并没有表明不同的行为。我在手册中没有看到“-x
”的任何其他相关引用。它没有描述启动顺序的差异。
澄清:在典型的Linux机器系统上,“/bin/sh
”是“/bin/bash
”的符号链接(或者找到Bash可执行文件的位置),这两个命令行实现了与执行跟踪一起运行脚本的等效效果。在其他系统(例如,Solaris和一些更现代的Linux变体)上,/bin/sh
不是Bash,并且两个命令行会给出(稍微)不同的结果。最值得注意的是,“/bin/sh
”会被Bash中的构造所混淆,它根本不会识别。 (在Solaris上,/bin/sh
是一个Bourne shell;在现代Linux上,它有时是Dash - 一个更小,更严格的POSIX-only shell。)当这样的名称调用时,'shebang'行('{{文件开头的1}}'vs #!/bin/bash
')对内容的解释方式没有影响。
Bash手册有一个关于Bash POSIX mode的部分,与此答案的长期但错误的版本相反(另见下面的评论),确实详细描述了'Bash调用为{的区别{1}}'和'Bash被调用为'#!/bin/sh
'。
在调试(Bash)shell脚本时,使用带有sh
选项的shebang行中命名的shell将是明智且理智的 - 甚至是必要的。否则,在运行脚本时进行调试时,您可能(将会?)获得不同的行为。
答案 1 :(得分:27)
我使用以下方法调试我的脚本。
如果任何外部程序返回非零退出状态, set -e
会立即停止脚本。如果您的脚本试图处理所有错误情况以及应该捕获失败的情况,这将非常有用。
set -x
,它肯定是所有调试方法中最有用的。
set -n
也可能有用。
strace
对于查看正在发生的事情也很有用。如果您没有自己编写脚本,则特别有用。
答案 2 :(得分:12)
此答案有效且有用:https://stackoverflow.com/a/951352
但是,我发现“标准”脚本调试方法效率低,不直观且难以使用。对于那些习惯于复杂的GUI调试器的人来说,这些调试器可以轻松解决所有问题(并且可能存在难题),这些解决方案并不十分令人满意。
我所做的是使用DDD和bashdb的组合。前者执行后者,后者执行脚本。这提供了一个多窗口UI,能够在上下文中逐步执行代码并查看变量,堆栈等,而无需不断的心理努力来维护头脑中的上下文或继续重新列出源代码。
答案 3 :(得分:9)
您也可以在脚本中编写“set -x”。
答案 4 :(得分:8)
我发现了shellcheck实用程序,可能有些人发现它很有趣 https://github.com/koalaman/shellcheck
一个小例子:
$ cat test.sh
ARRAY=("hello there" world)
for x in $ARRAY; do
echo $x
done
$ shellcheck test.sh
In test.sh line 3:
for x in $ARRAY; do
^-- SC2128: Expanding an array without an index only gives the first element.
修复bug,先试试......
$ cat test.sh
ARRAY=("hello there" world)
for x in ${ARRAY[@]}; do
echo $x
done
$ shellcheck test.sh
In test.sh line 3:
for x in ${ARRAY[@]}; do
^-- SC2068: Double quote array expansions, otherwise they're like $* and break on spaces.
让我们再试一次......
$ cat test.sh
ARRAY=("hello there" world)
for x in "${ARRAY[@]}"; do
echo $x
done
$ shellcheck test.sh
现在找到!
这只是一个小例子。
答案 5 :(得分:3)
答案 6 :(得分:2)
我构建了一个Bash调试器。试一试吧。我希望它会有所帮助 https://sourceforge.net/projects/bashdebugingbash
答案 7 :(得分:2)
设置+ x = @ECHO OFF,设置-x = @ECHO ON。
您可以向标准Shebang添加-xv
选项,如下所示:
#!/bin/bash -xv
-x
:显示命令及其执行的参数
-v
:在读取时显示shell输入行。
ltrace
是另一个类似于strace
的Linux实用程序。但是,ltrace
列出了在可执行文件或正在运行的进程中调用的所有库调用。它的名字来自于库调用跟踪。例如:
ltrace ./executable <parameters>
ltrace -p <PID>
答案 8 :(得分:1)
我认为您可以尝试使用此Bash调试器:http://bashdb.sourceforge.net/。
答案 9 :(得分:1)
使用带有炮弹的插件和eclipse; basheclipse 强>
https://sourceforge.net/projects/shelled/?source=directory https://sourceforge.net/projects/basheclipse/?source=directory
对于炮击:下载zip并通过帮助将其导入eclipse - &gt;安装新软件:本地存档对于basheclipse:将jar复制到eclipse的dropins目录
按照提供的https://sourceforge.net/projects/basheclipse/files/?source=navbar
步骤进行操作我在http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html
撰写了一个包含许多屏幕截图的教程答案 10 :(得分:0)
通过shell的全局变量来记录shell脚本有很多细节。我们可以在shell脚本中模拟类似的日志记录:http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html
该帖子详细介绍了INFO,DEBUG,ERROR等日志级别。 跟踪脚本输入,脚本退出,功能输入,功能退出等详细信息。
示例日志:
答案 11 :(得分:0)
set -[nvx]
除了
set -x
和
set +x
停止转储。
我想谈谈set -v
转储比较小的产出更少。
bash <<<$'set -x\nfor i in {0..9};do\n\techo $i\n\tdone\nset +x' 2>&1 >/dev/null|wc -l
21
for arg in x v n nx nv nvx;do echo "- opts: $arg"
bash 2> >(wc -l|sed s/^/stderr:/) > >(wc -l|sed s/^/stdout:/) <<eof
set -$arg
for i in {0..9};do
echo $i
done
set +$arg
echo Done.
eof
sleep .02
done
- opts: x
stdout:11
stderr:21
- opts: v
stdout:11
stderr:4
- opts: n
stdout:0
stderr:0
- opts: nx
stdout:0
stderr:0
- opts: nv
stdout:0
stderr:5
- opts: nvx
stdout:0
stderr:5
为了测试一些变量,我在某个时候使用它:
bash <(sed '18ideclare >&2 -p var1 var2' myscript.sh) args
添加:
declare >&2 -p var1 var2
在第18行并运行生成的脚本(使用 args ),而无需编辑它们。
当然,这可以用于添加set [+-][nvx]
:
bash <(sed '18s/$/\ndeclare -p v1 v2 >\&2/;22s/^/set -x\n/;26s/^/set +x\n/' myscript) args
将在第18行之后添加declare -p v1 v2 >&2
,在第22行之前添加set -x
,在第26行之前添加set +x
。
bash <(sed '2,3s/$/\ndeclare -p LINENO i v2 >\&2/;5s/^/set -x\n/;7s/^/set +x\n/' <(
seq -f 'echo $@, $((i=%g))' 1 8)) arg1 arg2
arg1 arg2, 1
arg1 arg2, 2
declare -i LINENO="3"
declare -- i="2"
/dev/fd/63: line 3: declare: v2: not found
arg1 arg2, 3
declare -i LINENO="5"
declare -- i="3"
/dev/fd/63: line 5: declare: v2: not found
arg1 arg2, 4
+ echo arg1 arg2, 5
arg1 arg2, 5
+ echo arg1 arg2, 6
arg1 arg2, 6
+ set +x
arg1 arg2, 7
arg1 arg2, 8
注意: 动态修改会影响对$LINENO
的关注!
(要查看生成的脚本而不执行,只需删除bash <(
和) arg1 arg2
)