如何调试bash脚本?

时间:2009-06-04 15:36:47

标签: bash shell

有没有办法调试bash脚本?例如,打印一种执行日志,如“呼叫线路1”,“呼叫线路2”等。

12 个答案:

答案 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,能够在上下文中逐步执行代码并查看变量,堆栈等,而无需不断的心理努力来维护头脑中的上下文或继续重新列出源代码。

有关于在此设置的指导:http://ubuntuforums.org/showthread.php?t=660223

答案 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)

安装VSCode,然后添加bash调试扩展,您就可以在可视模式下进行调试了。见Here正在行动。

enter image description here

答案 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>  

Source

答案 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

步骤进行操作

enter image description here

我在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等日志级别。 跟踪脚本输入,脚本退出,功能输入,功能退出等详细信息。

示例日志:

enter image description here

答案 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

一步一步,执行时间

查看 my answer about how to profile bash scripts