23

This question was motivated by Rmarkdown not outputting results of system command to html file. For some reason, the output of system() in R (or system2()) cannot be captured by sink() or capture.output(), so currently there is no way for knitr to record the output. For example, in the R console:

> system('ls')
DESCRIPTION
NAMESPACE
R
README.md
inst
man

but in a knitr document, you won't see the output, because capture.output(system('ls')) is character(0), i.e. the output cannot be captured. Of course I can do cat(system('ls', intern = TRUE), sep = '\n') as I mentioned in the answer of that question, but this is kind of awkward. I wonder if it is a way to capture the output of system() without using intern = TRUE and cat().


Update: see https://github.com/yihui/knitr/issues/1203 for a hack that I provided to solve the problem.

Community
  • 1
  • 1
Yihui Xie
  • 28,913
  • 23
  • 193
  • 419

2 Answers2

5

I don't think you can do this (at least on *nix systems; I don't have a Windows/Mac system handy) because system appears to invisibly return the value returned by the command executed, and R doesn't appear to redirect the command's output to the R console.

This is because the stdout of your terminal is not the same as the R console "stdout". What you see from within your R session is the mix of the terminal's stdout and the R process output. capture.output is looking for the R process' output, not all output to stdout from the parent process.

You can start a process that prints to stdout, put it in the background, then start R... and you'll see that process' output in your "R output", similar to if you had run system("ping -c5 8.8.8.8") from R.

josh@computer: /home/josh
> ping -c5 8.8.8.8 & R
[1] 5808
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=39.9 ms

R version 3.2.4 Revised (2016-03-16 r70336) -- "Very Secure Dishes"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> 64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=38.1 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=38.3 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=46 time=38.4 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=46 time=38.3 ms

--- 8.8.8.8 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4003ms
rtt min/avg/max/mdev = 38.176/38.656/39.986/0.703 ms

> q()
Save workspace image? [y/n/c]: n
[1]+  Done                    ping -c5 8.8.8.8

josh@computer: /home/josh
> 
Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
  • 2
    I don't think the invisible returned value is the issue. I expect `capture.output()` to capture stdout, although that is not what is documented (the documentation used a vague word "output" and I don't know if it implies stdout). For example, you can `capture.output((function() {print("Hello world!"); invisible("hi")})())` and it captures `Hello world!` from `print()`. My actual question is why the output of `print()` can be captured, but the output of `system()` can not. It is either `system()` does not write to stdout, or `capture.output()` does not really capture stdout. – Yihui Xie Apr 29 '16 at 18:10
  • @Yihui not that I'm an expert on R internals, but [this](https://github.com/wch/r-source/blob/e5b21d0397c607883ff25cca379687b86933d730/src/unix/sys-unix.c#L337) line _does_ seem to suggest `system` is using `stdout` (contrary to my expectations) – MichaelChirico Apr 29 '16 at 19:13
  • @MichaelChirico Thanks for digging into the C code. Then I guess my next question is what kind of output `capture.output()` really captures. – Yihui Xie Apr 29 '16 at 20:16
  • `capture.output` is an extended wrapper to `sink`... and I've got no idea what's going on in C [there](https://github.com/wch/r-source/blob/8496645d62db4d6d5ee3c7ad67a6fd4a1225e00e/src/main/connections.c#L4806-L4837) – MichaelChirico Apr 29 '16 at 20:18
  • @Yihui: sorry about that. It was late when I answered and I clearly missed the mark. See if my edit helps. – Joshua Ulrich Apr 29 '16 at 20:55
  • No worries. That example is very helpful. Thanks! – Yihui Xie Apr 30 '16 at 03:10
  • 1
    Very interesting. The question becomes, then, is there any scope for writing a more expansive version of `capture.output` that will intercept the terminal's `stdout` as well as the R console's? – MichaelChirico Apr 30 '16 at 20:59
5

You could add a function knitr::system that masks base::system. Users could work with it like it was system::base, but the output can be captured by capture.output:

system <- function(...) {
  stopifnot(!any(names(list(...)) %in% "intern"))
  result <- base::system(..., intern = TRUE)
  print(result)
}

I admit, that this is somewhat hacky, and to be honest, I'm not sure about possible side effects. But I think it could be worth a try.

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
CL.
  • 14,577
  • 5
  • 46
  • 73
  • Thanks! I was aware of this but I'd not use it if I can find a way to capture the output of `system()`. See my comment under Joshua Ulrich's answer. – Yihui Xie Apr 29 '16 at 18:12
  • @Yihui it's not *that* bad -- especially knowing that eg the `ignore.stdout` and `ignore.stderr` options in `system` simply adds `>/dev/null` or `2>/dev/null` redirection(s) to the `command` :) – daroczig Apr 30 '16 at 04:10
  • nice approach, my concern is that this solution waits till system has written everything into the result object, and prints it then as one chunk. If you want to monitor a longer system call that continuously writes into the sink-redirected console, it is not functional. I don't have another solution though – user3490026 Feb 24 '20 at 15:22