Existe uma maneira de `source ()` e continuar após um erro?

Eu tenho um grande script R de, digamos, 142 pequenas seções. Se uma seção falhar com um erro, eu gostaria que o script continuasse em vez de interrompê-lo. As seções não dependem necessariamente umas das outras, mas algumas dependem. Se um no meio falhar, tudo bem. Eu prefiro não apimentar esse script com as chamadas try() . E eu prefiro não ter que dividir o arquivo em muitos arquivos menores, já que cada seção é bem pequena.

Se source() pudesse funcionar como se tivesse sido copiado e colado no console R, isso seria ótimo. Ou se houvesse uma maneira de rebaixar o erro ao aviso, isso também seria ótimo.

Uma vez que o script foi executado, pretendo obter (ou similar) a saída para o texto de erro ou aviso, de modo que eu possa ver todos os erros ou avisos que ocorreram, não apenas que ele foi interrompido no primeiro erro.

Eu li a ?source e pesquisei a tag [R] do Stack Overflow. Eu encontrei as seguintes perguntas semelhantes, mas try e tryCatch foram as respostas fornecidas:

R Script – Como continuar a execução de código em erro
Existe alguma maneira de ter o script R continuar depois de receber mensagens de erro em vez de interromper a execução?

Eu não estou procurando try ou try tryCatch pelas razões acima. Isso não é para o teste de pacote R, onde estou ciente das estruturas de teste e onde muitas chamadas try() ou test_that() (ou semelhantes) são totalmente apropriadas. Isto é para outra coisa onde eu tenho um script como descrito.

Obrigado!

Para tornar isso mais concreto, que tal o seguinte?

Primeiro, para testar a abordagem, crie um arquivo (chame-o de "script.R" ) contendo várias instruções, a primeira das quais lançará um erro quando avaliada.

 ## script.R rnorm("a") x <- 1:10 y <- 2*x 

Em seguida, analise-o em uma lista de expressões e avalie cada elemento por vez, envolvendo a avaliação dentro de uma chamada para tryCatch() para que os erros não causem muito dano:

 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 

O pacote de avaliação fornece outra opção com sua function evaluate() . Não é tão leve quanto a minha outra sugestão, mas – como uma das funções que sustentam knitr – tem sido tão bem testada quanto você poderia esperar!

 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 

Para a saída que mais parece que você digitou as instruções na linha de comando, use replay() . (Graças a hadley pela dica):

 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 

Editar

replay() também oferece uma maneira melhor de reproduzir apenas os erros e avisos, se eles são tudo o que você precisa:

 ## 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 # > 

isto é desajeitado e não usa o amigo de ninguém eval(parse( mas pode ser um pouco útil .. a resposta de josh é muito mais limpa).

 # 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. }