如何调试 Bash 脚本? [关闭]
Posted
技术标签:
【中文标题】如何调试 Bash 脚本? [关闭]【英文标题】:How can I debug a Bash script? [closed] 【发布时间】:2010-10-31 09:10:21 【问题描述】:有没有办法调试 Bash 脚本?
例如,打印某种执行日志的内容,例如“调用第 1 行”、“调用第 2 行”等。
【问题讨论】:
这里也有类似的问题:serverfault.com/questions/16204/… Here maybe another duplicate. 【参考方案1】:将 Eclipse 与 Shelled 和 BashEclipse 插件结合使用。
DVKit - 用于设计验证任务的基于 Eclipse 的 IDE BashEclipse对于 Shelled:下载 ZIP 文件并通过菜单 Help 将其导入 Eclipse → 安装新软件:本地存档。对于 BashEclipse:将 JAR 文件复制到 Eclipse 的 dropins 目录中
按照 BashEclipse files
中提供的步骤进行操作我在 Bash: enabling Eclipse for Bash Programming | Plugin Shelled (shell editor)
写了一个包含许多屏幕截图的教程【讨论】:
这是一个边界线link-only answer(另见here)。您应该扩展您的答案以在此处包含尽可能多的信息,至少是实际完成您的建议所需的最低限度,并且仅使用链接作为参考。基本上,Stack Overflow(以及所有 Stack Exchange)上的帖子必须是独立的。这意味着您的答案中需要包含足够的信息,这样读者就不需要到场外寻找指示。目前,此答案并非如此。 这是我在查看了许多实际上表明真正的调试是可能的之后找到的第一个答案。标准答案“set +x”与自包含答案完美匹配,但几乎故意忽略有关真正调试的真正问题。我为这个答案鼓掌? dietrichschroff.blogspot.com 页面完全加载(或超时)非常缓慢。 第一个链接,https://sourceforge.net/projects/shelled/?source=directory
重定向到https://sourceforge.net/projects/dvkit/?source=directory
(“DVKit”),并且表面上没有似乎有任何事情可做”带壳”。一个解释将是有序的。
这和the first attempt有什么不同?【参考方案2】:
set +x = @ECHO OFF, set -x = @ECHO ON.
您可以将-xv
选项添加到标准shebang,如下所示:
#!/bin/bash -xv
-x
:在执行时显示命令及其参数。
-v
:在读取时显示 shell 输入行。
ltrace
是另一个类似于strace 的 Linux 实用程序。但是,ltrace
列出了在可执行文件或正在运行的进程中调用的所有库调用。它的名称本身来自库调用跟踪。
例如:
ltrace ./executable <parameters>
ltrace -p <PID>
Source
【讨论】:
【参考方案3】:安装Visual Studio Code,然后添加 Bash 调试扩展,您就可以在可视模式下进行调试了。查看here 的实际应用。
【讨论】:
【参考方案4】:调试Bash脚本的一些技巧:
使用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
。它会受到on-the-fly 修改的影响!
(要在不执行的情况下查看生成的脚本,只需删除 bash <(
和 ) arg1 arg2
)
一步一步,执行时间
看看my answer about how to profile Bash scripts。
【讨论】:
【参考方案5】:有大量关于通过 shell 的全局变量记录 shell 脚本的详细信息。我们可以在 shell 脚本中模拟类似的日志记录:Log tracing mechanism for shell scripts
这篇文章详细介绍了介绍日志级别,例如 INFO、DEBUG 和 ERROR。跟踪脚本入口、脚本出口、函数入口、函数出口等细节。
示例日志:
【讨论】:
这是您的博文吗?标题中有a typo。【参考方案6】:我找到了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.
修复错误。先试试吧……
$ 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
现在找到了!
这只是一个小例子。
【讨论】:
它是online! 幸运的是,该工具已经发展到可以找到剩余错误的程度。【参考方案7】:我构建了一个 Bash 调试器:Bash Debuging Bash。试试看吧。
【讨论】:
BDB 目前支持英语和西班牙语。要更改语言,请编辑文件 /etc/default/bdb 截图看起来很有趣,但我无法让它运行 "bdb.sh: line 32: bdbSTR[1]: unbound variable";顺便说一句,它会显示我们在代码上执行的每个步骤的所有设置变量的当前值吗?【参考方案8】:Jonathan Leffler's answer 有效且有用。
但是,我发现“标准”脚本调试方法效率低下、不直观且难以使用。对于那些习惯于复杂的 GUI 调试器,让一切都触手可及,让工作轻松解决简单问题(也可能解决难题)的人来说,这些解决方案并不是很令人满意。
我确实使用了DDD 和 bashdb 的组合。前者执行后者,后者执行你的脚本。这提供了一个多窗口用户界面,能够在上下文中单步执行代码并查看变量、堆栈等,而无需在头脑中维护上下文或不断重新列出源代码。
DDD and BASHDB 中有设置指南。
【讨论】:
感谢您的回答,刚刚发现 ddd。在 Ubuntu 12.04.3(64 位)中,apt-sources 版本不起作用。我必须从源代码编译和安装才能开始调试我的 bash 脚本。这里的说明 - askubuntu.com/questions/156906/… 有帮助。 是的,这是个问题。我用一些脚本解决了这个问题——“dddbash”安装/构建 DDD,如果错误则删除旧版本,安装 bashdb 等。(现在已使用此信息编辑了答案)【参考方案9】:sh -x script [arg1 ...]
bash -x script [arg1 ...]
这些可以让您跟踪正在执行的内容。 (另请参阅答案底部附近的“澄清”。)
有时,您需要在脚本中控制调试。在这种情况下,作为Cheetoreminded我,你可以使用:
set -x
这将打开调试。然后你可以再次关闭它:
set +x
(您可以通过分析$-
、x
的当前标志来找出当前的跟踪状态。)
此外,shell 通常提供选项“-n
”用于“不执行”和“-v
”用于“详细”模式;你可以结合使用这些来查看 shell 是否认为它可以执行你的脚本——如果你在某个地方有一个不平衡的引用,这偶尔会很有用。
有人认为 Bash 中的“-x
”选项与其他 shell 不同(请参阅 cmets)。 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' 行('#!/bin/bash
' vs '#!/bin/sh
') 在文件开头对内容的解释方式没有影响。
Bash 手册中有一个关于 Bash POSIX mode 的部分,与该答案的长期存在但错误的版本相反(另请参见下面的 cmets),它确实详细描述了 'Bash 调用为 sh
之间的区别' 和 'Bash 调用为 bash
'。
在调试 (Bash) shell 脚本时,使用带有 -x
选项的 shebang 行中命名的 shell 将是明智和理智的——甚至是必要的。否则,您在调试时可能(将会?)得到与运行脚本时不同的行为。
【讨论】:
他确实指定了bash
脚本。使用 sh -x
运行 bash 脚本会导致它的行为完全不同!请更新您的答案。
@lhunath:'sh -x'(或'bash -x')以什么方式使脚本的行为完全不同?显然,它将跟踪信息输出到stderr;这是给定的(尽管我的回答中没有提到)。但还有什么?我在 Linux 和 MacOS X 上使用 'bash' 作为 'sh' 并没有发现严重的问题。
在启动和运行时存在差异。它们在 Bash 发行版中有完整的记录。
这里是 bash 文档的链接:gnu.org/software/bash/manual/bashref.html#Bash-Startup-Files '如果使用名称 sh 调用 Bash,它会尽可能地模仿 sh 的历史版本的启动行为,同时符合posix 标准”
并使用 PS4 提示符提供更多有用信息,例如:export PS4='+($BASH_SOURCE:$LINENO): $FUNCNAME[0]:+$FUNCNAME[0](): '
【参考方案10】:
我想你可以试试这个 Bash 调试器:http://bashdb.sourceforge.net/。
【讨论】:
有大量关于通过 shell 的全局变量记录 shell 脚本的详细信息。我们可以在 shell 脚本中模拟类似的登录:cubicrace.com/2016/03/efficient-logging-mechnism-in-shell.html【参考方案11】:我使用了以下方法来调试我的脚本。
set -e
使脚本在任何外部程序返回非零退出状态时立即停止。如果您的脚本尝试处理所有错误情况并且应该捕获失败的情况,这将非常有用。
set -x
上面提到过,肯定是所有调试方法中最有用的。
如果您想检查脚本的语法错误,set -n
也可能很有用。
strace
对于查看正在发生的事情也很有用。如果您还没有自己编写脚本,则特别有用。
【讨论】:
跟踪脚本(即跟踪执行脚本的 shell)是一种奇怪的 shell 调试方法(但可能适用于有限的问题)。 我承认它很奇怪也很冗长,但如果你将 strace 的输出限制为几个系统调用,它就会变得有用。 请注意,如果您还想在脚本启动的进程中查找错误,则需要strace -f
。 (这使得它更加冗长,但如果您将其限制为您感兴趣的系统调用,它仍然很有用)。
set -e
是...controvercial。【参考方案12】:
您也可以在脚本中编写“set -x”。
【讨论】:
你可以写'set +x'来关闭它。以上是关于如何调试 Bash 脚本? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章