寻找一个选项,让我可以将 R 诊断消息(由
message()
生成)重定向到 stdout
,而不是默认情况下的 stderr
。
message
手动状态:
默认处理程序将消息发送到 stderr() 连接。
所以问题是如何改变这种默认行为?仍然保留
warning()
和 stop()
的重定向完好无损。
已经尝试过接收器
type='message'
,但它会重定向所有内容(消息、警告、错误)。
如果有人愿意测试,这是示例脚本
exec_test.R
:
print("using print")
cat("using cat\n")
message("using message")
warning("using warning")
stop("using stop")
q("no")
然后将由以下人员执行:
Rscript exec_test.R 1>> exec_test.Rout 2>> exec_test_error.Rout
我不知道该使用什么
2>&1
,因为我的脚本会产生大量的message
,而且很少出现真正的错误,所以我需要将这些日志存储在单独的文件中。
使用
sink
。这是对您的代码的修改:
sink(stdout(), type = "message") # sink messages to stdout
print("using print")
cat("using cat\n")
message("using message")
warning("using warning")
sink(NULL, type="message") # close the sink
warning("after ending sink") # this will be the only thing in your err file
q("no")
OP 显示了通过 Rscript 命令并使用一些 I/O 重定向进行的执行。如果您想使用重定向来记录所有内容并仅在出现错误时显示到控制台,我发现的最佳方法是使用
||
在打印到屏幕之前检查脚本是否具有非零退出状态:
Rscript myrscript.R > temp.log 2>&1 || cat temp.log
此方法严格依赖于打印的退出代码,它只能部分绕过
message()
进入 stderr,但我认为这个示例有助于提及,因为消息不一定会触发非零退出状态,您可以继续用这种方法安静地记录。
如果您想更进一步并继续附加到单个日志文件,那么这将起作用:
Rscript myrscript.R > temp.log 2>&1 || cat temp.log && cat temp.log >> persistent.log && rm temp.log
该命令的伪代码是:
stderr
和 stdout
重定向到 temp.log虽然这很可能不是最佳实践,但您可以使用默认写入
message
的版本覆盖 stdout()
,对吗?
message <- function (..., domain = NULL, appendLF = TRUE)
{
args <- list(...)
cond <- if (length(args) == 1L && inherits(args[[1L]], "condition")) {
if (nargs() > 1L)
warning("additional arguments ignored in message()")
args[[1L]]
}
else {
msg <- .makeMessage(..., domain = domain, appendLF = appendLF)
call <- sys.call()
simpleMessage(msg, call)
}
defaultHandler <- function(c) {
cat(conditionMessage(c), file = stdout(), sep = "")
}
withRestarts({
signalCondition(cond)
defaultHandler(cond)
}, muffleMessage = function() NULL)
invisible()
}
将@Thomas 和@branch14 的答案合二为一,这就是我的想法:
.stderr_message <- message
message <- function(...) {
sink(stdout(), type = "message")
.stderr_message(...)
sink(NULL, type = "message")
}
我使用此代码在 shell 上验证了这一点
.stderr_message <- message
message <- function(...) {
sink(stdout(), type = "message")
.stderr_message(...)
sink(NULL, type = "message")
}
print(1)
message(1)
没有我的
message()
:
> Rscript bug.R 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stdout: [1] 1
stderr: 1
> Rscript bug.R 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stdout: [1] 1
stdout: 1
这可能是一种解决方法,而不是推荐的解决方案,但应该是满足问题确切需求的最佳匹配。另一种方法是使用 C++ 钩子,但这超出了我的能力范围。
使用 @bers 代码,我们可以在会话期间替换基本 R 命名空间函数,我们可以在全局范围内更改警告和消息的行为。
我们可以创建函数,然后使用以下方法替换名称空间中的基本 R 函数:
.stderr_message <- message
message <- function(...) {
sink(stdout(), type = "message")
.stderr_message(...)
sink(NULL, type = "message")
}
assignInNamespace("message", message, "base")
现在我们可以测试消息了:
# message go to stdout()
library("dplyr") ## this should redirect the mask messages to stdout
我们可以对警告消息执行相同的操作,但对于警告,我们应该添加
imediate. =TRUE
,否则它将收集所有消息并使用另一个函数(我不知道),该函数将使用默认接收器到 stderr。
.stderr_warning <- warning
warning <- function(...) {
sink(stdout(), type = "message")
.stderr_warning(..., immediate. = TRUE)
sink(NULL, type = "message")
}
assignInNamespace("warning", warning, "base")
现在警告:
# warning go to stdout()
optFunc <- function(x) {
abs(x^4 + x^2)
}
root <- optim(-1, optFunc)
但这仅适用于使用 R 的
base::warning
实现的函数,而不是来自 .Call
或 rcpp
的 C 警告,因为它们从 C 接口而不是 R env 调用直接警告和错误消息。一个例子:
## warning go to stderr()
as.numeric(c("1", "NA"))
## this error and warning to stderr()
readRDS("noFile")