在R中进行系统调用时,如何捕获退出状态和输出

22

我在玩一些 system()system2() 的小例子,发现可以将输出或退出状态保存到一个对象中。这是一个玩具例子:

X <- system("ping google.com",intern=TRUE)

给我输出,然而

X <- system2("ping", "google.com")

使用system2调用ping命令无法成功(返回值为1)。如果我想要获取输出和退出状态,我需要做两个系统调用,这似乎有点过度。有没有办法只使用一个系统调用即可同时获取两者呢?

编辑:如果可能的话,我希望在控制台中同时获取输出和退出状态,而不是通过在system2调用中使用stdout="somefile.ext"并随后读取文件来实现。


你是用Linux还是Windows?我甚至无法在Windows上让stdout="somefile.ext"正常工作,但在Linux上它很好用... - Tommy
我建议你在标签中加入“Linux”,以及你正在使用的任何 shell。这样可以吸引来自操作系统专家的一些解决方案。 - Iterator
抱歉向OP和@Gavin道歉,我可能错了:我认为这是明确或隐含的Linux,但我看到OP甚至没有提到Linux,我不知道它可能是另一个操作系统。 - Iterator
不是Linux,甚至不具有特定的操作系统。这是关于R代码的问题。 - Joris Meys
3个回答

16

从R 2.15开始,当stdout和/或stderr为TRUE时,system2将以属性的形式返回返回值。这使得获取文本输出和返回值变得容易。

在此示例中,ret最终将成为一个带有属性"status"的字符串:

> ret <- system2("ls","xx", stdout=TRUE, stderr=TRUE)
Warning message:
running command ''ls' xx 2>&1' had status 1 
> ret
[1] "ls: xx: No such file or directory"
attr(,"status")
[1] 1
> attr(ret, "status")
[1] 1

13

您的system2函数描述有些令人困惑,因为它具有stdout和stderr参数。因此,它能够返回退出状态、标准输出和标准错误输出。

> out <- tryCatch(ex <- system2("ls","xx", stdout=TRUE, stderr=TRUE), warning=function(w){w})
> out
<simpleWarning: running command ''ls' xx 2>&1' had status 2>
> ex
[1] "ls: cannot access xx: No such file or directory"
> out <- tryCatch(ex <- system2("ls","-l", stdout=TRUE, stderr=TRUE), warning=function(w){w})
> out
 [listing snipped]                  
> ex
 [listing snipped]

5
+1 非常好。顺便说一句,欢迎来到SO,这里并不是所有人都会阅读R命令的帮助信息。;-) - Iterator
+1,因为它使用了R内部的警告和错误技巧。我可以从警告消息中剪切出退出状态。我知道我可以使用stdout=TRUE,但退出状态是我的主要关注点。这就是为什么我没有那样做的原因。 - Joris Meys
似乎这是做法,所以这将成为被接受的答案。 - Joris Meys
当我运行第一个命令 (ls xx) 时,我得到了关于 out 的相同警告信息,但是 ex 根本没有设置。这是在 Mac 和 Linux 上都使用 R 2.14.2。有其他的方法吗? - wch

9
我建议在这里使用这个函数:
robust.system <- function (cmd) {
  stderrFile = tempfile(pattern="R_robust.system_stderr", fileext=as.character(Sys.getpid()))
  stdoutFile = tempfile(pattern="R_robust.system_stdout", fileext=as.character(Sys.getpid()))

  retval = list()
  retval$exitStatus = system(paste0(cmd, " 2> ", shQuote(stderrFile), " > ", shQuote(stdoutFile)))
  retval$stdout = readLines(stdoutFile)
  retval$stderr = readLines(stderrFile)

  unlink(c(stdoutFile, stderrFile))
  return(retval)
}

这只在接受 > 和 2> 表示法的类 Unix shell 上工作, cmd 参数本身不应该重定向输出。但它能解决问题:

> robust.system("ls -la")
$exitStatus
[1] 0

$stdout
 [1] "total 160"                                                      
 [2] "drwxr-xr-x  14 asieira  staff    476 10 Jun 18:18 ."            
 [3] "drwxr-xr-x  12 asieira  staff    408  9 Jun 20:13 .."           
 [4] "-rw-r--r--   1 asieira  staff   6736  5 Jun 19:32 .Rapp.history"
 [5] "-rw-r--r--   1 asieira  staff  19109 11 Jun 20:44 .Rhistory"    

$stderr
character(0)

1
谢谢,这个很好用!这应该是R的默认功能。 - thc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接