【问题标题】:How to get R script line numbers at error?R脚本行号错误?
【发布时间】:2010-11-29 13:53:44
【问题描述】:

如果我从命令行(R --slave script.R)运行一个长的 R 脚本,我怎样才能让它在错误时给出行号?

如果可能的话,我不想在脚本中添加调试命令——我只想让 R 的行为与大多数其他脚本语言一样......

【问题讨论】:

  • 有什么更新吗?四 4 年后,尽管所有主流都采用了 R,但问题似乎仍然存在。
  • 我还有一个很长的 R 脚本,有很多小输出,我想像这样打印 (下划线)(下划线)LINE/FILE(下划线)(下划线) (行号和脚本名) C,而不是将行号硬编码到源代码中。
  • 我不知道 R 内部是否真的有“行号”的概念。但是,它确实具有完整任务的概念,即顶级任务。例如,可以很容易地定义一个任务处理程序来告诉一个顶级任务失败了。当然,对于那些有大链或大条件语句的人来说,这并不是很大的安慰。

标签: debugging r


【解决方案1】:

这不会给你行号,但它会告诉你调用堆栈中发生故障的位置,这非常有帮助:

traceback()

[编辑:]从命令行运行脚本时,您将不得不跳过一两个调用,请参阅traceback() for interactive and non-interactive R sessions

我不知道没有通常的调试嫌疑人的另一种方法:

  1. 调试()
  2. 浏览器()
  3. options(error=recover) [后跟 options(error = NULL) 恢复它]

You might want to look at this related post.

[Edit:] 抱歉...刚刚看到您正在从命令行运行它。在这种情况下,我建议使用 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) })(请参阅已接受答案的评论)。我认为将其添加到此处的答案中而不是仅提供指向另一个线程的链接是有意义的。
  • 一个新选项,让您可以在回溯github.com/aryoda/tryCatchLog中获取行号
【解决方案2】:

R 2.10 及更高版本将支持此功能。 Duncan Murdoch 刚刚于 2009 年 9 月 10 日在 r-devel 上发布了关于 findLineNum and setBreapoint 的帖子:

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

这些依赖于在代码中包含源参考调试信息。 这是source() 读取的代码的默认设置,但不是包的默认设置。 要获取包代码中的源引用,请设置环境 变量R_KEEP_PKG_SOURCE=yes,或在R中,设置 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 已经这样做了)。
  • 如果没有在 R 脚本中四处寻找,我真的不明白它是如何从命令行工作的
  • @hirse:这几乎是你的老答案了。你到底为什么要重新格式化它以假装我在引用?我不是,您的更改反映了我的意图。
  • “邓肯·默多克刚刚发布:”听起来很像引用,但如果不正确,请恢复编辑。我想让它对自己更具可读性,并且在我完成之前没有检查日期。如果整个答案过于过时,您也可以将其删除,以免给未来的读者造成混淆。
  • 你能恢复它吗?谢谢。
【解决方案3】:

执行options(error=traceback) 可提供有关导致错误的行内容的更多信息。如果出现错误,它会导致出现回溯,并且对于某些错误,它具有行号,前缀为 #。但它的命中或错过,许多错误不会得到行号。

【讨论】:

  • 不太适合我。我只有一个文件,它没有显示行号,只是在错误后说No traceback available
【解决方案4】:

你通过设置来做到这一点

options(show.error.locations = TRUE)

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

【讨论】:

  • 有关此选项的背景信息,请参阅stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
  • 这曾经可以工作,但由于不可靠而被禁用。我认为这是试图强迫您使用最终将是非免费的 RStudio。
  • 我对此表示怀疑。 R core 和 RStudio 是非常不同的组织,尤其是 R core 是坚定的开源者。
  • 在 CentOS 6.9, R-3.4.2 上工作
  • 也许值得一提的是,您应该在采购任何代码之前预先设置选项。
【解决方案5】:

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

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

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

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

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

    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 中给出的代码 sn-p 已放置在 myMainScript.R 的顶部,用于设置非交互式 R 会话的错误处理选项。
  4. 在执行myMainScript.R 时遇到错误。这可能在主脚本本身中,或者嵌套在几个函数深处。当遇到错误时,将按照步骤 1 中的说明进行处理,R 会话将终止。
  5. 在全局错误处理选项设置中'~/myUsername/directoryForDump' 指定的目录中创建名为errorDump.rda 的RData 转储文件和名为error.log 的错误日志。
  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. 您可以在闲暇时使用load('~/path/to/errorDump.rda')errorDump.rda 加载到交互式R 会话中。加载后,调用debugger(errorDump) 在任何活动环境中浏览内存中的所有 R 对象。有关详细信息,请参阅 debugger() 上的 R 帮助。

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

【讨论】:

  • 不知何故这不再有效。 error.log 在 R 4.0 中不再包含行号或文件名信息(其他版本我没试过)。
【解决方案6】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多