交互式和非交互式R会话的traceback()

33

我观察到交互式和非交互式R会话关于traceback()存在差异,但我不明白原因。对于以下代码,它将产生一个错误,但在交互式的R会话中,我可以看到回溯信息,而如果我将代码保存到test.R并通过Rscript test.RR -f test.R调用它,我将无法再看到回溯:

f = function() {
  on.exit(traceback())
  1 + 'a'
}
f()

在一个交互式的R会话中:

> f = function() {
+   on.exit(traceback())
+   1 + 'a'
+ }
> f()
Error in 1 + "a" : non-numeric argument to binary operator
1: f()

非交互式执行:

$ Rscript test.R 
Error in 1 + "a" : non-numeric argument to binary operator
Calls: f
No traceback available 
Execution halted

我在?traceback中没有看到解释,想知道是否有办法在非交互式的R会话中启用traceback。谢谢!


tracebackbaseenv() 中寻找一个名为 .Traceback 的对象。从 src/main/errors.c 看来,只有在 R_Interactive || haveHandler 等其他条件成立时才会创建它。如果没有 .Traceback,你将会得到消息 "No traceback available"。在 ?traceback 下也有一个警告提到了 .Traceback - BenBarnes
如果您只是在脚本中设置options(error=traceback)并删除on.exit调用,就可以达到所需的效果。尽管由于错误的“步骤”,它会创建重复。 - Brandon Bertelsen
据我所知,即使在非交互式会话中发生错误,仍然无法访问回溯信息,尽管调用堆栈会被返回。 @BrandonBertelsen - BenBarnes
http://r.789695.n4.nabble.com/Automatic-traceback-td845199.html - Brandon Bertelsen
@BenBarnes 您的评论是一个有用的答案,是否考虑将其添加为答案? - Gavin Simpson
4个回答

36

使用 traceback() 函数的默认参数值,将在 baseenv() 中寻找名为 .Traceback 的对象,以获取有关调用堆栈的信息。从 src/main/errors.c 看来,只有当条件之一为 R_Interactive || haveHandler 时才会创建 .Traceback 对象,这表明在非交互式会话期间不会创建该对象。如果没有名为 .Traceback 的对象,则会收到消息“无回溯可用”。

但是,在向 traceback() 函数的 x 参数传递非空值时,可以从非交互式会话中获取有关调用堆栈的信息。通过传递非零整数值(表示要跳过堆栈中的调用次数),将调用 c 级别函数 (R_GetTraceback) 来调查调用堆栈,而不是查找 .Traceback

因此,在非交互式会话中,有几种方法可以获取回溯信息:

f = function() {
  on.exit(traceback(1))
  1 + 'a'
}
f()

或者按照Brandon Bertelsen的建议设置options

options(error=function()traceback(2))

两个示例中传递给x的不同值解释了要跳过的函数数量不同。

  1. on.exit示例中,traceback(1)跳过了对traceback()的调用。

  2. 在设置options的示例中,有一个额外的匿名函数调用traceback(),这也应该/可以被跳过。

在原始帖子的示例中,与非交互式会话中出现错误时提供的自动回溯相比,使用traceback()并不能获得更多信息。但是,对于需要(并被传递)参数的函数,使用traceback()将比非交互式会话中呈现的调用堆栈标准报告更具信息性。


非常感谢!我没有意识到将x设置为数字也适用于非交互式的R会话。 - Yihui Xie
9
注意,使用options(error=function() traceback(2))会导致脚本在出现错误时仍能成功退出!解决方案是改用error=function(){ traceback(2); if(!interactive()) quit("no", status=1, runLast=FALSE) }(这里使用的停止参数来自于默认错误行为)。 - dshepherd
这仍然有些问题。当使用 options(error=function()traceback(2)) 时,会导致异常输出。请参见 https://stackoverflow.com/questions/52933667/r-executes-code-in-function-when-there-is-a-syntax-error-and-function-is-not-exp?noredirect=1#comment92778349_52933667 - irritable_phd_syndrome

5

还有一种可能性是将调试信息装入并稍后加载它们(参见好的?debugger帮助页面和相关主题的评论)。

例如,通过:

options(error = quote(dump.frames("testdump", TRUE)))

...

load("testdump.rda")
debugger(testdump)

或者
options(error = quote({dump.frames(to.file = TRUE); q(status = 1)}))

2
< p >使用options(error = function()stop("Error message"))可以避免脚本继续执行,这样更加安全。< /p > < p >以下是示例:< /p >
options(error = function() { 
  traceback(2)
  options(error = NULL)
  stop("exiting after script error") 
})

这将正确打印堆栈跟踪信息,然后正常退出。


这种方法是否会产生一个退出代码来指示程序失败了? - carbocation
1
是的,因为它通过stop()退出。 - Karl Forner
感谢您。深入了解文档确认:在交互使用中,缺省的行为(即空错误处理程序)是返回到顶层提示符或顶层浏览器,在非交互使用中,则(实际上)调用q("no", status = 1, runLast = FALSE) - carbocation

1

BenBarnes的答案和dshepherd的评论救了我一命。我将添加一个参数以避免屏幕过度拥挤,以防其中一个参数非常大。

options(error=function(){traceback(2,max.lines=3);if(!interactive())quit("no",status=1,runLast=FALSE)})

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