22

I've been playing a bit with system() and system2() for fun, and it struck me that I can save either the output or the exit status in an object. A toy example:

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

gives me the output, whereas

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

gives me the exit status (1 in this case, google doesn't take ping). If I want both the output and the exit status, I have to do 2 system calls, which seems a bit overkill. How can I get both with using only one system call?

EDIT : I'd like to have both in the console, if possible without going over a temporary file by using stdout="somefile.ext" in the system2 call and subsequently reading it in.

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
Joris Meys
  • 106,551
  • 31
  • 221
  • 263
  • Are you using Linux or Windows? I can't even get stdout="somefile.ext" to work on Windows, but it works fine on Linux... – Tommy Aug 10 '11 at 18:37
  • I recommend adding `linux` to your tags, along with whatever shell you're using. This can invite some solutions from the OS experts. – Iterator Aug 10 '11 at 19:53
  • Apologies to OP and @Gavin, I may be mistaken: I thought this was explicitly or implicitly Linux, but I see the OP didn't even mention Linux and it could be another OS for all I know. – Iterator Aug 10 '11 at 20:51
  • It's not Linux, it's even non OS specific. It's about the R code. – Joris Meys Aug 11 '11 at 11:27

3 Answers3

16

As of R 2.15, system2 will give the return value as an attribute when stdout and/or stderr are TRUE. This makes it easy to get the text output and return value.

In this example, ret ends up being a string with an attribute "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
wch
  • 4,069
  • 2
  • 28
  • 36
13

I am a bit confused by your description of system2, because it has stdout and stderr arguments. So it is able to return both exit status, stdout and 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]
Ido Tamir
  • 3,017
  • 2
  • 19
  • 28
  • 5
    +1 Very good. By the way, welcome to SO, where not everyone reads the help information for R commands. ;-) – Iterator Aug 10 '11 at 20:55
  • +1, as it's using R's internal warning and error tricks. I can cut out the exit status from the warning-message. I know I can use stdout=TRUE, but the exit status is my main concern. That's why I didn't do that. – Joris Meys Aug 11 '11 at 11:33
  • Seems like the way to do it anyway, so that's going to be the accepted answer. – Joris Meys Dec 15 '11 at 13:40
  • When I run the first command (`ls xx`), I get the same warning message for `out`, but `ex` is not set at all. This is with R 2.14.2 on both Mac and Linux. Are there any other ways to do this? – wch Mar 19 '12 at 20:14
9

I suggest using this function here:

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)
}

This will only work on a Unix-like shell that accepts > and 2> notations, and the cmd argument should not redirect output itself. But it does the trick:

> 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)
asieira
  • 3,513
  • 3
  • 23
  • 23
  • 1
    Thanks, this works great! This really should be the default functionality in R. – thc Dec 28 '18 at 23:31