我向来把调试工作放在编程环节的首位,因此当使用一门新的语言开始实际项目时,就愿意花精力摸索它的调试工具的使用。当了解到几门调试工具以后,发现大部分的地方是相通的。
现代 IDE 天然自带图形化的调试工具,例如 PyCharm 调试 Python. 但这篇文章并不关注于此,反而只列举命令行下的调试方案。诚然,IDE 是好用的,如果你用 IDE 开发你的主项目,并且熟悉它,就应该使用 IDE 提供的调试方案。但这并不代表你不该了解本文中的内容,至少有以下原因:
- IDE 太重了,而你想要有一个轻量级的调试方案。
- 如果你接触了一门新在函数栈中往上一层语言,你势必不想一开始就陷入 IDE 方面的问题。
- 有时候你进入一个新环境,而它没有提供你想要的 IDE 的选项,本文的方案就可以作为一个备选。
我认为,调试工具主要关注三方面的能力(排名有先后):
- 断点调试
- 分步迭代
- 监视器
所以,再轻量的工具,也势必要从以上三方面进行考察。如果工具提供的命令过于复杂,没关系,搞清楚这三个再说。
本文将考察主流脚本语言的调试工具,它们是 Python、Ruby、NodeJS. 我说过,在调试工具方面,大部分的理念是相通的。所以我将在 Python 一节详细介绍,Ruby、NodeJS 一节简略介绍对位命令。
Python
Python 内建了调试工具,可以在代码的任何位置打断点。首先,在代码中引入 Python 的调试工具:
from pdb import set_trace
然后,在你想要打断的位置,调用 set_trace
方法:
a = 1
set_trace()
b = a + 1
当程序执行到第二行代码的地方,就会在这个位置中断,进入一个交互式的命令行环境。你可以在这个环境下执行很多的命令。
首先,可以在这个环境下使用print
命令查看变量的值。例如我想要看变量 a
的值,只需输入:
print(a)
实际上,print
只是 Python 的一个函数,你可以在该环境下调用任何的 Python 语句。
接下来,通过一些分步迭代的命令,我可以逐步地控制程序的运行。分步迭代的命令包括:
-
n(ext)
:执行到下一行的位置 -
s(tep)
:步进到下一个的位置 -
u(p)
:函数栈中的指针往上一层 -
d(own)
:函数栈中的指针往下一层
以上命令的括号标记用以指示别名,例如 next
命令,n
和 next
调用均可。
如果结束了当前的调试,需要程序继续运行,调用 c(ont(inue))
命令(支持三种写法:c
、cont
、continue
)。它会让程序继续运行,直到遇到下一个 set_trace()
的地方中断。
遗憾的是,我并没有在 Python 的 pdb 工具包中发现监视器的内容。好在作为优先级排在第三位的需求,少了它还是可以工作的。
Ruby
Ruby 语言也有类似的工具,Ruby 语言可以使用 pry
起到断点调试的效果。
首先,安装 pry
:
$ gem install pry
然后,在代码中引入 pry
,需要断点的位置调用 binding.pry
方法即可:
require 'pry'
a = 1
binding.pry
b = a + 1
pry
本身没有提供分步迭代的能力,另一款工具 byebug
支持分步迭代。但是 byebug
的交互式环境没有 pry
友好,其不支持语法高亮,并失去一些优化的特性。好在有人将 pry
和 byebug
的能力结合起来,创造了 pry-byebug
,兼具两者的优点。
我建议,直接安装 pry-byebug
:
$ gem install pry-byebug
引入和打断点的方式不变。但已经支持从 byebug 那里借来的分步迭代的能力,分步迭代的命令包括:
next
step
up
down
注意,没有缩略的别名,你需要完完整整地输入 next
命令才能达到效果。
从断点处的恢复执行的命令同样是:continue
.
pry
支持监视器的能力,对位的命令是 watch
. 下面是一个简单的计算 1 到 100 的加法的 Ruby 代码:
s = 0
binding.pry
(1..100).each do |i|
s += i
binding.pry
end
在第一个断点处,执行命令 watch s
,即可达到监视变量 s
的效果。监视器的作用是这样的,一旦监视的变量发生了改变,它会主动在控制台显示变量的值以示提醒。但 pry
有一个小脾气,你不 Enter
它不显示,它只会在命令行键入 Enter
键以后触发 watch 显示机制。
在以上的代码中,如果我们希望在每一次变量 s
加上 i
之后查看 s
的变化,需要重复调用下面两个命令:
continue
-
(这是键盘上的一个按键)
NodeJS
Node 环境下也有一样的基于命令行的调试工具。与 Python 和 Ruby 不同的是,它不是通过引入模块和调用方法的方式设置中断环境的,而是通过 debugger
这个关键字。
在 Node 中设置中断环境,需要做两步操作。
-
在代码中用
debugger
关键字:a = 1 debugger b = a + 1
- 调用的时候使用
node inspect source.js
命令。
程序会在代码的最开始处停住,需要调用 continue
命令(可简写为 c
或 cont
)才会开始执行。调用 continue
命令之后,程序会持续执行,知道遇到 debugger
的位置。这时候,就可以停下来好好欣赏程序的环境了。
停在 debugger
位置时,进入的是一个命令环境。此时输入 repl
命令进入一个调试环境,在这个环境下可以输入变量的名称以及表达式显示它们的值。一旦调试表达式结束,输入 Ctrl + C
退出调试环境回到命令环境。
如果你觉得切换调试环境和命令环境较为繁琐,有一些轻量级的方法用以受限的场景。如果你只想查看变量 sum
的值,或者表达式 a+1
的值,可借助 exec
命令:
debug> exec sum
debug> exec a + 1
调试器支持分步迭代和监视器,命令列举如下:
next
step
out
watch
值得一提的是,没有了 up
和 down
命令,取而代之的是 out
. out
的意思是执行完当前函数的内容,回到上一层调用该函数的位置。