R脚本行号有误吗?


105

如果我从命令行(R --slave script.R)运行一个较长的R脚本,如何获取错误的行号?

我根本不想在脚本中添加调试命令-我只希望R像大多数其他脚本语言一样工作...


31
任何更新?四4年后,似乎问题依然存在,尽管所有的主流采用R的
桂安布罗斯

我也有一个很长的R脚本,其中有很多小输出,我想像在C中那样打印(下划线)(下划线)LINE / FILE(下划线)(下划线)(行号和脚本名),而不是对行号进行硬编码成源。
mosh

我不知道R内部是否真的有“行号”的概念。但是,它确实具有完整任务的概念,即顶级任务。例如,可以轻松定义一个任务处理程序,以告诉哪个顶级任务失败。当然,对于那些拥有大型连锁店或大型条件陈述的人来说,这并不是很大的安慰。
russellpierce

Answers:


45

这不会提供行号,但是会告诉您调用堆栈中失败的位置,这非常有帮助:

traceback()

[编辑:]从命令行运行脚本时,您将不得不跳过一两个调用,有关交互式和非交互式R会话,请参见traceback()

如果没有通常的调试怀疑,我不知道另一种方法:

  1. debug()
  2. 浏览器()
  3. options(error = recover)[后接options(error = NULL)以将其还原]

您可能需要查看此相关文章。

[编辑:]对不起...只是看到您正在从命令行运行它。在那种情况下,我建议您使用options(error)功能。这是一个简单的例子:

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

您可以在错误情况下根据需要创建详细的脚本,因此您只需要确定调试所需的信息即可。

否则,如果您担心某些特定区域(例如,连接到数据库),则将它们包装在tryCatch()函数中。


第一个[Edit:]块中链接的解决方案对我有效。最好的方法似乎是@dshepherd的注释,即添加options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(请参阅已接受答案的注释)。我认为将其添加到此处的答案中而不是仅提供到另一个线程的链接是有意义的。
cryo111

1
一个新的选项,让您在回溯github.com/aryoda/tryCatchLog中
–lunuini

13

这样做options(error=traceback)提供了有关导致错误的行内容的更多信息。如果存在错误,则会导致出现回溯,对于某些错误,它的行号以开头#。但是无论碰到还是错过,很多错误不会得到行号。


2
不太适合我。我只有一个文件,它没有显示行号,只是No traceback available在错误发生后才说。
Mark Lakata

11

R 2.10及更高版本将对此提供支持。邓肯·默多克(Duncan Murdoch)于2009年9月10日发布在r-devel上,关于findLineNum和setBreapoint

我刚刚在R-devel中添加了几个函数来帮助调试。 findLineNum()查找哪个函数的哪一行对应于特定的源代码行;setBreakpoint()接受的输出findLineNum,并调用trace()在那里设置断点。

这些依赖于在代码中具有源引用调试信息。这是读取的代码的默认值source(),而不是软件包的默认值。要获取包代码中的源引用,请设置环境变量R_KEEP_PKG_SOURCE=yes,或在R中设置set options(keep.source.pkgs=TRUE),然后从源代码安装包。阅读?findLineNum有关如何告诉它在程序包中进行搜索的详细信息,而不是将搜索限制在全局环境中。

例如,

x <- " f <- function(a, b) {
             if (a > b)  {
                 a
             } else {
                 b
             }
         }"


eval(parse(text=x))  # Normally you'd use source() to read a file...

findLineNum("<text>#3")   # <text> is a dummy filename used by
parse(text=)

这将打印

 f step 2,3,2 in <environment: R_GlobalEnv>

你可以使用

setBreakpoint("<text>#3")

在此处设置断点。

代码中仍然存在一些局限性(可能还有错误)。我会修复


谢谢。刚刚也注册了r-devel邮件列表。我一直在避免使用r-help,因为它会阻塞我的收件箱(r-sig-finance已经做到了)。
Shane

1
不真正在R脚本中
闲逛

1
@hirse:这几乎是您的旧答案。为什么在地球上您重新格式化它以假装我在引用?我不是,而且您的更改没有反映我的意图。
Dirk Eddelbuettel

“ Duncan Murdoch刚刚发布:”听起来很像是一个引号,但是如果不正确,请恢复编辑。我想让自己更容易理解它,直到完成后才检查日期。如果整个答案过时,您也可以删除它,以免引起将来读者的困惑。
聘请

你能恢复吗?谢谢。
Dirk Eddelbuettel

6

您可以通过设置

options(show.error.locations = TRUE)

我只是想知道为什么该设置不是R中的默认设置?应该如此,就像其他所有语言一样。


1
有关此选项的背景信息,请参见stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda

1
这曾经可以工作,但是由于不可靠而被禁用。我认为这是一种迫使您使用RStudio的尝试,而RStudio最终将是非免费的。
埃里克·莱斯钦斯基

6
我对此表示怀疑。R core和RStudio是非常不同的组织,特别是R core是坚定的开源人员。
Ben Bolker

在CentOS 6.9,R-3.4.2上工作
irritable_phd_syndrom,

也许值得一提,在采购任何代码之前,您应该预先设置选项。
JAponte,

3

指定全局R选项来处理非灾难性错误对我来说很有效,并且自定义工作流用于保留有关错误的信息并在故障后检查此信息。我当前正在运行R版本3.4.1。下面,我提供了适用于我的工作流程的描述,以及一些我用来在R中设置全局错误处理选项的代码。

根据我的配置,错误处理还会在错误发生时创建一个RData文件,其中包含工作内存中的所有对象。可以使用将该转储读回到R load(),然后可以使用交互检查发生错误时存在的各种环境debugger(errorDump)

我将注意到,我能够traceback()从堆栈中的任何自定义函数的输出中获取行号,但前提是我keep.source=TRUE在调用source()脚本中使用的任何自定义函数时使用了该选项。如果没有此选项,则按如下所示设置全局错误处理选项会将的完整输出发送traceback()到名为的错误日志中error.log,但是行号不可用。

这是我在工作流程中采取的一般步骤,以及在非交互式R失败后如何访问内存转储和错误日志。

  1. 我将以下内容放在从命令行调用的主脚本的顶部。这将为R会话设置全局错误处理选项。我的主脚本叫做myMainScript.R。代码中的各行之后都有注释,描述了它们的作用。基本上,使用此选项时,当R遇到触发错误的错误时stop(),它将在目录中的所有活动环境中创建工作内存的RData(* .rda)转储文件,~/myUsername/directoryForDump并且还将错误日志命名error.log为一些有用信息。同一目录。您可以修改此代码段,以增加对错误的其他处理(例如,将时间戳记添加到转储文件和错误日志文件名等)。

    options(error = quote({
      setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths.
      dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session.
      sink(file="error.log"); # Specify sink file to redirect all output.
      dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file.
      cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace.
      cat('\nTraceback:');
      cat('\n');
      traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls.
      sink();
      q()}))
  2. 确保从主脚本和任何后续函数调用中,无论何时获取函数,keep.source=TRUE都使用该选项。也就是说,要使用函数,可以使用source('~/path/to/myFunction.R', keep.source=TRUE)。这是traceback()输出包含行号所必需的。看来您也可以使用来全局设置此选项options( keep.source=TRUE ),但我尚未对其进行测试以查看其是否有效。如果不需要行号,则可以省略此选项。

  3. 从终端(R外部),使用批处理模式调用主脚本Rscript myMainScript.R。这将启动一个新的非交互式R会话并运行脚本myMainScript.R。已放置在步骤1顶部的步骤1中给出的代码段myMainScript.R设置了非交互式R会话的错误处理选项。
  4. 在执行时遇到错误myMainScript.R。这可以在主脚本本身中,也可以嵌套在多个函数中。遇到错误时,将按照步骤1中的指定执行处理,R会话将终止。
  5. 在全局错误处理选项设置所指定的目录中创建名为RData转储文件errorDump.rda和和名为错误日志error.log的目录'~/myUsername/directoryForDump'
  6. 在您闲暇时,检查error.log以查看有关错误的信息,包括错误消息本身以及导致错误的完整堆栈跟踪。这是错误时生成的日志的示例;请注意,字符后的#数字是调用堆栈中各个点处的错误的行号:

    Error in callNonExistFunc() : could not find function "callNonExistFunc"
    Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF
    
    Traceback:
    3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304
    2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352
    1: test_multi_commodity_flow_cmd(config_file_path = config_file_path, 
    spot_file_path = spot_file_path, forward_file_path = forward_file_path, 
    data_dir = "../", user_dir = "Output", sim_type = "spot", 
    sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw", 
    nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31", 
    compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes, 
    overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime, 
    ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0)
  7. 您可以在闲暇时errorDump.rda使用加载到交互式R会话中load('~/path/to/errorDump.rda')。加载后,调用debugger(errorDump)以浏览任何活动环境中内存中的所有R对象。有关debugger()更多信息,请参见R帮助。

在某些类型的生产环境中运行R时,如果您在命令行中启动了非交互式R会话,并且希望保留有关意外错误的信息,则此工作流程将非常有用。将内存转储到文件中的功能,可以在发生错误时检查工作内存,并且在调用堆栈中具有错误的行号,有助于快速地对导致错误的原因进行事后调试。


0

首先,options(show.error.locations = TRUE)然后traceback()。错误行号将显示在#之后

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.