Есть ли способ `source()` и продолжить после ошибки?
У меня есть большой R script, скажем, 142 небольших раздела. Если один раздел завершился с ошибкой, я бы хотел, чтобы script продолжал, а не останавливался. Секции не обязательно зависят друг от друга, но некоторые делают. Если кто-то посередине терпит неудачу, это нормально. Я бы предпочел не перенести этот script на вызовы try()
. И я бы предпочел не разделить файл на несколько небольших файлов, так как каждый раздел довольно короткий.
Если source()
можно заставить работать так, как если бы он был скопирован и вставлен в консоль R, это было бы здорово. Или, если есть способ понизить ошибку до предупреждения, это тоже будет хорошо.
После запуска script я намерен grep (или аналогичный) вывод для текста с ошибкой или предупреждением, чтобы я мог видеть все ошибки или предупреждения, которые произошли, а не только то, что он остановился на первой ошибке.
Я прочитал ?source
и искал тег Qaru [R]. Я нашел следующие похожие вопросы, но ответы try
и tryCatch
были предоставлены:
R script - Как продолжить выполнение кода при ошибке
Есть ли способ, чтобы R script продолжался после получения сообщений об ошибках вместо остановки выполнения?
Я не ищу try
или tryCatch
по указанным выше причинам. Это не для тестирования пакетов R, где я знаю рамки тестирования и где многие вызовы try()
или test_that()
(или аналогичные) являются полностью подходящими. Это для чего-то еще, где у меня есть script, как описано.
Спасибо!
Ответы
Ответ 1
Чтобы сделать это более конкретным, как насчет следующего?
Во-первых, чтобы проверить подход, создайте файл (назовите его "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
Ответ 2
Пакет оценивать предоставляет еще одну опцию с помощью функции 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()
. (Спасибо hadley за подсказку):
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
# >
Ответ 3
Это неудобно и не использует друга eval(parse(
, но может быть несколько полезным. Ответ на josh намного чище.
# 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 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 an actual error and needs to be just plain skipped.
if ( !grepl( 'unexpected end of input' , err ) ) j <- i + 1
# otherwise it an "end of line" error, which just means j alone.
}