我正在运行一个 bash 脚本,其中包含一个将运行数千次的 for 循环。在这个 for 循环中的某个地方,代码经常会卡住(我见过它停止的最低迭代次数是 32,大多数停止次数都在 4000 次左右 - 它应该运行 5000 次,有时它会完成任务)。我想调试一下这个问题。显然,我不想在命令窗口中手动输入数十万行代码并等待它被卡住的地方。
有没有一种简单的方法可以找出它执行或尝试执行的最后一行是什么? (请记住,当出现问题时,终端似乎被冻结 - 有时整个系统停止工作,需要进行远程重置才能再次启动。)我认为这可能是 echo 的一个功能。我发现这个问题和答案但我不确定“当命令从解析器的角度表示脚本中的特定行时”意味着什么,因此它是否适用。我也对这段代码的实际作用感到非常困惑,如果这段代码对我有帮助,我将不胜感激。
这就是我正在寻找的答案吗?这个问题还有其他解决方案吗?
答案1
首先,您应该尝试确定问题发生在哪个迭代中。如果你幸运的话,那么它总是一样的。如果您无论如何都不这样做,那么您应该计算迭代次数并打印它们或将它们写入文件:
exec 3>/my/logfile
iter=0
while whatevercondition; do
((iter++))
echo "$iter" >&3
done
如果问题总是发生在同一次迭代中,那么您应该激活调试输出,然后:
CRASH_ITER=12345
iter=0
while whatevercondition; do
((iter++))
if [ "$iter" -eq "$CRASH_ITER" ]; then
set -vx
fi
done
不可重现的错误
如果无法将错误缩小到某个迭代,那么您可以让 bash 将完整的调试输出写入文件:
bash -vx ./script.sh 2>debug.txt
答案2
如果将其添加到 bash 脚本的顶部:
函数DEBUG_TRAP() { 排版-p BASH_SOURCE;排版-p BASH_LINENO;排版-p FUNCNAME } 陷阱 DEBUG_TRAP INT
您可以通过向进程发送 INT 信号来获取有关程序位置的信息:
kill -INT process-number
您可以在 bash 脚本中获取进程号,如下所示:
回声$$
如果执行此操作后没有获得任何输出,则说明您的脚本已重定向其输出,或者脚本挂在进程内。 lsof 会告诉您有关输出重定向的信息附注将提供有关进程状态的信息。
答案3
如果你的循环看起来像:
i=0
while [ "$((i+=1))" -le 5000 ] &&
set >iter.log
do monte_carlo
done
每次迭代都会覆盖一个文件,其中每次都会列出所有 shell 变量的当前值。所以如果它在运行 488 时中断,你就会有一个迭代日志文件将 的值记录$i
为 488 以及迭代开始时的所有其他 shell 变量。
要跟踪失败的行号,您可以更进一步:
i=0 PS4='$LINENO : '; set -x
while [ ... ] &&
set >iter.log
do ...
done 2>>iter.log
这仍然会在每次迭代时覆盖该文件,但它还会为执行的每一行附加调试输出,并将其行号包含在文件中。