如何调试Ruby脚本

173

我从互联网上复制了以下Ruby代码,并进行了一些更改,但它无法正常工作。

我该如何自行调试程序?


8
投票重新开放。原帖明显想要类似GDB的单步调试功能。 - Ciro Santilli OurBigBook.com
请查看“debug.rb” - 由Ruby核心团队开发的新的Ruby调试工具。 - Marian13
为了得到更加实时的答案,展示如何使用 pry-byebugpry 来暂停、检查、继续执行、进入和退出方法,请查看我在类似问题中的回答 - software_writer
17个回答

159

使用Pry (GitHub)。

通过以下方式进行安装:

$ gem install pry
$ pry
然后添加:
require 'pry'; binding.pry

然而,在pry 0.12.2中,没有像nextbreak这样的导航命令。一些其他的gems也提供了这个功能,例如pry-byebug


11
我建议你使用 Pry(绝对是改变生活的一件好事!)... 安装并在你的程序中引用后,设置断点就像写 binding.pry 一样简单。它还带有颜色自动完成、文档查找和动态编辑以及重新加载方法的功能。 - Andrea Fiore
5
Pry的主页链接在这里:http://pryrepl.org/。 - shadowbq
5
Prybyebug都很好,但不是你调试的第一步。在大多数情况下,使用raise object.inspect引发异常比打开irb会话更快地解决你的问题。我建议只有在更简单的解决方案(如引发异常)无法解决问题时才使用控制台调试器。 - Kelsey Hannan
3
有没有一种方法可以使用pry逐行调试代码?我找不到如何实现;这是我对于一个调试器的期望。 - jpetazzo
2
@jpetazzo 是的,通过输入 'next'。 - marcbest
显示剩余2条评论

114
  1. 在 Ruby 中:

    ruby -rdebug myscript.rb 
    

    然后,

    • b <line>:设置断点
    • 以及n(ext)s(tep)c(ontinue)
    • p(uts)用于显示

    (类似于perl调试)

  2. 在Rails中:使用以下命令启动服务器

    script/server --debugger
    

    在代码中添加debugger


7
-rdebug 是垃圾,而且没有维护。使用 Pry(参见其他答案)。 - Snowcrash
3
你为什么说“-r debug”是垃圾? - sid smith
9
使用-rdebug:无需更改源文件即可进行调试。 - germanlinux
3
对于一个新的Ruby/Rails应用程序,Pry是正确的选择。但我花了一个多小时尝试找到一个古老的版本的Pry,以便在一个要求特定版本facets的Rails 2.2应用程序上运行,并且没有成功。对于古老的Rails应用程序,ruby-debug有些棘手但可以完成工作。 - Abe Voelker

57

如Banister所建议的:使用pry!我完全同意。

Pry比IRB更好用,这点毋庸置疑。

您需要添加:

require 'pry'

将其添加到您的源文件中,

然后通过在源代码中添加断点来调试。

binding.pry

在您想查看事物的地方 (这就像在经典IDE环境中触发断点)

一旦您的程序命中

binding.pry

你将被抛入pry repl中,直接拥有程序的所有上下文, 这样你就能够轻松地探索周围的一切、调查所有对象、改变状态,甚至动态修改代码。

我相信你不能更改当前所在方法的代码, 因此你无法悲伤地改变下一行要执行的代码。 但好的 Ruby 代码通常都是单行的;-)


38

通过抛出异常来调试 比通过阅读 print 日志语句更容易,并且对于大多数错误,它通常比打开像 prybyebug 这样的 irb 调试器快得多。这些工具不应始终是您的第一步。


快速调试 Ruby/Rails:

1. 快速方法:抛出一个 Exception 然后使用 .inspect 查看其结果

调试 Ruby(尤其是 Rails)代码最快的方法是在调用方法或对象(例如 foo)时沿着代码执行路径抛出异常并同时调用 .inspect

raise foo.inspect
在上面的代码中,raise引发一个Exception,它会停止代码的执行,并返回一个错误消息,其中包含关于你正在尝试调试的行上的对象/方法(即foo)的.inspect信息。这种技巧对于快速检查对象或方法(例如,它是否为nil?)以及立即确认给定上下文中是否执行了一行代码非常有用。

2.备选方案:使用ruby的IRB调试器,例如byebugpry

只有在您了解代码执行流状态的信息后,才应考虑转到类似prybyebug这样的ruby gem irb调试器,从而可以更深入地研究执行路径中的对象状态。

初学者通用建议

当您尝试调试问题时,一个好的建议是始终:认真阅读错误消息(RTFM)
这意味着在采取行动之前要仔细完整地阅读错误消息,以便您理解它想要告诉您的内容。调试时,在阅读错误消息时,请按以下顺序提出以下心理问题:
  1. 错误引用了哪个类?(即,我的对象类是否正确,或者我的对象为nil?)
  2. 错误引用了哪个方法?(即,是否在该方法中有类型问题;我可以在此类型/类的对象上调用此方法吗?)
  3. 最后,使用我从前两个问题中推断出来的内容,应该调查哪些代码行?(请记住:堆栈跟踪中的最后一行代码不一定是问题所在。)
在堆栈跟踪中特别注意来自您的项目的代码行(例如,如果您使用Rails,则以app/...开头的行)。99%的时间,问题出现在您自己的代码中。
为了说明按照这个顺序解释的重要性......
例如,一个使许多初学者困惑的Ruby错误消息:
@foo = Foo.new

...

@foo.bar

如果你收到以下错误信息:

undefined method "bar" for Nil:nilClass

初学者看到这个错误会认为问题在于方法bar未定义。但事实并非如此。实际上,这个错误中真正重要的部分是:

for Nil:nilClass

for Nil:nilClass意味着@foo是空值(Nil)! @foo不是Foo实例变量!你有一个空值对象。当你看到这个错误时,它只是Ruby想告诉你方法bar不存在于空值(Nil)类的对象中(好吧!因为我们试图使用Foo而不是Nil类的对象的方法)。

不幸的是,由于这个错误的表述方式(undefined method "bar" for Nil:nilClass),很容易让人误以为这个错误与bar未定义有关。如果没有仔细阅读,这个错误会导致初学者错误地深入研究Foobar方法的细节,完全忽略了错误提示对象是错误的类(在这种情况下:空值)的部分。仔细阅读错误消息可以轻松避免这种错误。

总结:

在开始任何调试之前,始终仔细阅读完整的错误消息。这意味着:始终先检查错误消息中对象的类型,然后是其方法,在你开始深入研究可能发生错误的堆栈跟踪或代码行之前。花上5秒钟可以为你节省5个小时的挫败感。

简明扼要:不要盯着打印日志看:引发异常或使用irb调试器。在调试之前仔细阅读错误信息,避免陷入死胡同。


3
虽然我同意你的观点,认为阅读打印日志很繁琐,但我认为建议第一时间不使用调试器是非常糟糕的建议。添加断点并触发它与引发异常的工作量完全相同,但它可以让你全面查看应用程序的当前状态。如果在检查可疑状态时出现任何进一步的问题,你可以交互式地进行深入分析,而无需添加另一个异常并重新启动应用程序。 - Patrick R.
2
@PatrickR。根据我的经验,在你仍在努力查找代码中问题所在的确切位置时,IRB调试器并不是一个好的第一步。添加和删除许多IRB断点比添加异常需要更多的时间,并且不能像异常一样为初学者提供关于代码控制流的明确答案,以排除错误的假设。IRB断点也很容易被遗忘,导致请求挂起时产生混乱。如果您确切地知道问题所在并需要调试其状态,那么可以使用IRB调试器。但通常情况下并非如此。 - Kelsey Hannan
1
哈!阅读错误信息对于 Ruby 来说是有帮助的,但是 其他 脚本语言呢?我不能责怪提问者甚至没有尝试。 - kayleeFrye_onDeck
1
我的初始反应类似于@PatrickR.,但我还是试了一下。最终,我发现这是错误的建议,或者换个角度看,是面向不同用例的建议。特别是在某些情况下,(a)不使用Rails和(b)产生错误结果但不会引发任何错误或异常,即代码计算了一个数字,只不过是错误的数字。尝试迭代猜测在哪里进行编程是一种策略,但这需要更多的工作,因为我必须不断重新启动和重新运行。每个周期都有自己的启动时间是浪费的。 - Brick

21
  1. 尽可能地输出变量内容进行调试。(这被称为 printf 调试)。您可以通过运行以下代码实现:

    STDERR.puts x.inspect
    
    或者
    STDERR.puts "Variable x is #{x.inspect}"
    
    如果您想要更容易地输入代码,那么您可能想使用exemplor gem。

  2. 打开警告。如果您正在运行ruby,则使用-w开关运行它(例如ruby -w script.rb)。如果您在irb中运行它,并且您使用的是1.9.2之前的版本,请在会话开始时键入$VERBOSE=true。如果您拼错了一个实例变量,一旦警告打开,您将收到以下提示:

    warning: instance variable @valeus not initialized

  3. 理解二分查找的概念(以下引用来自Practices of an Agile Developer

    将问题空间划分为两半,并查看哪一半包含问题。然后再将该半部分分成两半,并重复此过程。

  4. 如果您对二分查找成功,您可能会发现有一行代码没有按照您的预期执行。例如:

    [1, 2, 3].include?([1,2])
    

    即使你认为它应该返回true,但其实它的值是false。在这种情况下,你可能需要查看文档。文档网站包括ruby-doc.org或者APIdock。对于后一种情况,你可以在右上角放大镜旁边输入include?,选择其下方带有Arrayinclude?(如果你不知道[1, 2, 3]是什么类别,请在irb中输入[1, 2, 3].class),然后你会进入到include? (Array),其中详细介绍了它的功能。

    然而,如果文档无法帮助你,你最好能够提出一个具体行为有问题的问题,而非询问整个脚本出现了什么问题。


7
我刚刚发现了这个宝石(将Pry转换为MRI Ruby 2.0+的调试器)
安装方式如下:

https://github.com/deivid-rodriguez/pry-byebug

gem install pry-byebug

然后像使用pry一样使用它,在你想要中断的那一行进行标记:

require 'pry'; binding.pry

与普通的pry不同,这个gem具有一些关键的类似于GDB的导航命令,例如nextstepbreak

break SomeClass#run            # Break at the start of `SomeClass#run`.
break Foo#bar if baz?          # Break at `Foo#bar` only if `baz?`.
break app/models/user.rb:15    # Break at line 15 in user.rb.
break 14                       # Break at line 14 in the current file.

7
为了轻松调试Ruby shell脚本,只需将其第一行更改为:
#!/usr/bin/env ruby

至:

#!/usr/bin/env ruby -rdebug

每次调试控制台出现时,您可以选择:

  • c表示继续执行(到下一个异常、断点或带有debugger的行),
  • n表示执行下一行,
  • w/where表示显示框架/调用堆栈,
  • l表示显示当前代码,
  • cat表示显示catchpoints。
  • h表示更多帮助。

另请参见:使用ruby-debug进行调试, ruby-debug gem的快捷键


如果脚本只是挂起,并且您需要回溯,请尝试使用lldb/gdb,例如:

echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -nf ruby)

然后检查你的进程前台。

如果效果更好,将lldb替换为gdb。在非所属进程上调试时,请加前缀sudo


1
ruby-debug看起来非常过时。ruby-debug的Git仓库今年只有一次提交——它还在积极维护吗? - Andrew Grimm
它似乎也随 Ruby 一起打包,当你需要时非常方便。好答案! - Breedly

6

所有其他答案已经几乎给出了一切...只是稍作补充。

如果您想要一些更类似于IDE的调试器(非CLI),并且不害怕使用Vim作为编辑器,我建议您使用Vim Ruby Debugger插件。

它的文档非常简单明了,所以请跟随链接查看。简而言之,它允许您在编辑器中设置当前行的断点,在暂停时在漂亮的窗口中查看本地变量,进行步进/转至 - 几乎所有通常的调试器功能。

对我来说,使用这个vim调试器为调试Rails应用程序非常愉快,尽管Rails的丰富的记录器能力几乎消除了对它的需求。


6

删除所有的东西

欢迎来到2017年 ^_^

好的,如果你不反对尝试一个新的IDE,你可以免费地按照以下步骤进行。

快速指南

  1. 安装vscode
  2. 如果你还没有安装过,请安装Ruby Dev Kit
  3. 为vscode安装Ruby、ruby-linter和ruby-rubocop扩展
  4. 根据需要手动安装rubyide/vscode-ruby specifies中列出的任何gems
  5. 配置你的launch.json使用"cwd"以及"program"字段,使用{workspaceRoot}
  6. 添加一个名为"showDebuggerOutput"的字段,并将其设置为true
  7. 在你的调试首选项中启用到处都可以断点,如"debug.allowBreakpointsEverywhere": true

详细说明

  1. 下载 Visual Studio Code,也称为 vscode;这不同于 Visual Studio。它是免费的、轻量级的,并且通常被认为是积极的。
  2. 安装 Ruby Dev Kit;你应该按照其存储库中的说明进行操作:https://github.com/oneclick/rubyinstaller/wiki/Development-Kit
  3. 接下来,您可以通过Web浏览器或IDE内部安装扩展;这是针对IDE内部的。如果您选择另一个选项,可以转到 此处。导航到vscode的扩展部分;您可以使用几种方法来完成此操作,但最具未来性的方法可能是按下 F1 并键入 ext,直到出现一个名为Extensions: Install Extensions的选项。其他方法是使用 CtrlShiftx 或从顶部菜单栏中选择 View->Extensions
  4. 接下来,您将需要以下扩展;这些不是100%必需的,但在您进行了一些修改后,我会让您决定保留什么:
    • Ruby;扩展作者 Peng Lv
    • ruby-rubocop;扩展作者 misogi
    • ruby-linter;扩展作者 Cody Hoover
  5. 在您的Ruby脚本目录中,我们将通过命令行创建一个名为 .vscode 的目录,并在其中放置一个名为 launch.json 的文件,我们将在其中存储一些配置选项。
    • launch.json 内容

{ "version": "0.2.0", "configurations": [ { "name": "本地文件调试", "type":"Ruby", "request": "launch", "cwd": "${workspaceRoot}", "program": "{workspaceRoot}/../script_name.rb", "args": [], "showDebuggerOutput": true } ] }

  1. 按照扩展程序作者的说明进行手动 gem 安装。暂时可以在此处找到:https://github.com/rubyide/vscode-ruby#install-ruby-dependencies
  2. 您可能希望随时能够设置断点,否则未启用此选项可能会导致混淆。为此,我们将前往顶部菜单栏并选择 文件->首选项->设置(或 Ctrl,),然后滚动到 调试 部分。展开它并查找一个名为 "debug.allowBreakpointsEverywhere" 的字段——选择该字段并单击类似铅笔的小图标,将其设置为 true
在完成所有有趣的事情后,您应该能够设置断点并在类似于2017年中期和更暗的主题的菜单中进行调试:enter image description here 带有所有有趣的东西,例如调用堆栈,变量查看器等。
最大的烦恼是1)安装先决条件和2)记住配置 .vscode \ launch.json 文件。只有#2应该为未来的项目增加任何负担,并且您可以复制一个足够通用的配置,例如上面列出的配置。可能有一个更一般的配置位置,但我不知道。

现在已经是2018年了,但不幸的是,您仍然无法使用您在此处列出的插件来调试单元测试...相当遗憾。 - MonsieurDart
1
@MonsieurDart 当我写这个的时候,它是用于基本调试 Ruby 脚本的。我不熟悉任何关于单元测试的具体内容。如果这个答案不正确或过时了,请让我知道需要注意什么以及任何可以加快这个过程的信息。 - kayleeFrye_onDeck
嘿@kayleeFrye_onDeck!你的回答很好,我完全同意。但是,上次我检查Peng Lv的Ruby插件是否能够在单元测试(或任何其他测试)中设置断点时,它无法实现。这是该插件已知的限制之一。我认为人们在尝试配置他们的VSC实例之前需要知道这一点:这需要时间,并且对于许多人来说,运行测试是编写和调试代码的首选方法。 - MonsieurDart

6
我强烈推荐这个视频,以便在调试代码时选择适当的工具。

https://www.youtube.com/watch?v=GwgF8GcynV0

个人而言,我认为这个视频涉及两个重要的主题。

  • Pry 对于调试数据非常棒,“pry 是一个数据探索器”(原话如此)
  • 调试器似乎更适合逐步调试。

以上是我的个人看法!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接