31

我有一个大的 R 脚本,比如 142 个小部分。如果一个部分因错误而失败,我希望脚本继续而不是停止。这些部分不一定相互依赖,但有些部分确实如此。如果中间有一个失败,那没关系。我不想在这个脚本中加入try()调用。而且我不希望将文件分成许多较小的文件,因为每个部分都很短。

如果source()可以让它像在 R 控制台上复制和粘贴一样工作,那就太好了。或者,如果有办法将错误降级为警告,那也可以。

脚本运行后,我打算对错误或警告文本的输出进行 grep(或类似操作),以便我可以看到所有发生的错误或警告,而不仅仅是它在第一个错误时停止。

我已经阅读?source并搜索了 Stack Overflow 的 [R] 标签。我发现了以下类似的问题,但是trytryCatch提供了答案:

R 脚本 - 如何在错误时继续执行代码
有没有办法让 R 脚本在收到错误消息后继续执行而不是停止执行?

我不是在寻找trytryCatch出于上述原因。这不适用于 R 包测试,我知道测试框架以及许多try()ortest_that()调用(或类似调用)是完全合适的。这是为了我有描述的脚本的其他东西。

谢谢!

4

3 回答 3

26

为了使这一点更具体,以下内容如何?

首先,为了测试该方法,创建一个"script.R"包含多个语句的文件(调用它),其中第一个语句在评估时会引发错误。

## script.R

rnorm("a")
x <- 1:10
y <- 2*x

然后将其解析为一个表达式列表,并依次评估每个元素,将评估包装在一个调用中,tryCatch()以便错误不会造成太大的损害:

ll <- parse(file = "script.R")

for (i in seq_along(ll)) {
    tryCatch(eval(ll[[i]]), 
             error = function(e) message("Oops!  ", as.character(e)))
}
# Oops!  Error in rnorm("a"): invalid arguments
# 
# Warning message:
# In rnorm("a") : NAs introduced by coercion
x
# [1]  1  2  3  4  5  6  7  8  9 10
y
# [1]  2  4  6  8 10 12 14 16 18 20
于 2013-01-30T19:55:56.157 回答
15

评估包为其evaluate()功能提供了另一种选择。它不像我的其他建议那么轻巧,但是——作为knitr的基础功能之一——它已经过了你所希望的那样的良好测试!

library(evaluate)

rapply((evaluate(file("script.R"))), cat)  # For "script.R", see my other answer
# rnorm("a")
# NAs introduced by coercionError in rnorm("a") : invalid arguments
# In addition: Warning message:
# In rnorm("a") : NAs introduced by coercion
x
#  [1]  1  2  3  4  5  6  7  8  9 10
y
#  [1]  2  4  6  8 10 12 14 16 18 20

对于看起来更像您在命令行中实际键入语句的输出,请使用replay(). (感谢哈德利的提示):

replay(evaluate(file("script.R")))
# >
# > rnorm("a")
# Warning message:
# NAs introduced by coercion
# Error in rnorm("a"): invalid arguments
# > x <- 1:10
# > y <- 2*x

编辑

replay()如果您只需要这些,还提供了一种更好的方法来回放错误和警告:

## Capture the output of evaluate()
res <- evaluate(file("script.R"))

## Identify those elements inheriting from "error" or "warning
ii <- grepl("error|warning", sapply(res, class))   

## Replay the errors and warnings
replay(res[ii])
# Warning message:
# NAs introduced by coercion
# Error in rnorm("a"): invalid arguments
# > 
于 2013-01-30T20:42:40.790 回答
5

这很笨拙,并且没有使用任何人的朋友eval(parse(,但可能会有所帮助..乔希的回答要干净得多。

# assign the filepath
fn <- "c:/path/to your/script.R"

# read in the whole script
z <- readLines( fn )

# set a starting j counter
j <- 1

# loop through each line in your script..
for ( i in seq(length(z)) ) {

    # catch errors
    err <- try( eval( parse( text = z[j:i] ) ) , silent = TRUE )

    # if it's not an error, move your j counter up to one past i
    if ( class( err ) != 'try-error' ) j <- i + 1 else

    # otherwise, if the error isn't an "end of input" error,
    # then it's an actual error and needs to be just plain skipped.
    if ( !grepl( 'unexpected end of input' , err ) ) j <- i + 1

    # otherwise it's an "end of line" error, which just means j alone.
}
于 2013-01-30T20:03:10.760 回答