121

I have been using R CMD BATCH my_script.R from a terminal to execute an R script. I am now at the point where I would like to pass an argument to the command, but am having some issues getting it working. If I do R CMD BATCH my_script.R blabla then blabla becomes the output file, rather than being interpreted as an argument available to the R script being executed.

I have tried Rscript my_script.R blabla which seems to pass on blabla correctly as an argument, but then I don't get the my_script.Rout output file that I get with R CMD BATCH (I want the .Rout file). While I could redirect the output of a call to Rscript to a file name of my choosing, I would not be getting the R input commands included in the file in the way R CMD BATCH does in the .Rout file.

So, ideally, I'm after a way to pass arguments to an R script being executed via the R CMD BATCH method, though would be happy with an approach using Rscript if there is a way to make it produce a comparable .Rout file.

Bryce Thomas
  • 10,479
  • 26
  • 77
  • 126

6 Answers6

132

My impression is that R CMD BATCH is a bit of a relict. In any case, the more recent Rscript executable (available on all platforms), together with commandArgs() makes processing command line arguments pretty easy.

As an example, here is a little script -- call it "myScript.R":

## myScript.R
args <- commandArgs(trailingOnly = TRUE)
rnorm(n=as.numeric(args[1]), mean=as.numeric(args[2]))

And here is what invoking it from the command line looks like

> Rscript myScript.R 5 100
[1]  98.46435 100.04626  99.44937  98.52910 100.78853

Edit:

Not that I'd recommend it, but ... using a combination of source() and sink(), you could get Rscript to produce an .Rout file like that produced by R CMD BATCH. One way would be to create a little R script -- call it RscriptEcho.R -- which you call directly with Rscript. It might look like this:

## RscriptEcho.R
args <- commandArgs(TRUE)
srcFile <- args[1]
outFile <- paste0(make.names(date()), ".Rout")
args <- args[-1]

sink(outFile, split = TRUE)
source(srcFile, echo = TRUE)

To execute your actual script, you would then do:

Rscript RscriptEcho.R myScript.R 5 100
[1]  98.46435 100.04626  99.44937  98.52910 100.78853

which will execute myScript.R with the supplied arguments and sink interleaved input, output, and messages to a uniquely named .Rout.

Edit2:
You can run Rscript verbosely and place the verbose output in a file.

Rscript --verbose myScript.R 5 100 > myScript.Rout
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • 8
    I also get the impression `R CMD BATCH` is a relic. The thing I like about it though is that it produces an `.Rout` file that includes not just the script output, but also interleaves the input commands/comments from the `.R` script file that produced that output. – Bryce Thomas Jan 05 '13 at 01:08
  • 1
    I hear you. That was (I guess still is!) a nice aspect of `R CMD BATCH`. – Josh O'Brien Jan 05 '13 at 01:47
  • 5
    but I think you can do better than `R CMD BATCH` with `knitr`, e.g. `Rscript -e "knitr::stitch(commandArgs(TRUE)[1])" my_script.R` (you can replace `stitch` with `stitch_rhtml` or `stitch_rmd`, and you need to install `knitr` from [Github](https://github.com/yihui/knitr) since I just found a bug in `stitch`...) – Yihui Xie Jan 05 '13 at 04:08
  • 1
    @Yihui -- Cool. I'll have a look at that. I had a similar idea to use `Rscript` to execute a script that used `sink(outFile, split = TRUE)` and then `source(srcFile, echo = TRUE)` to produce an equivalent of .Rout. Posted it here for a few minutes before rolling back out of concerns that might be an unsafe way to use `sink()`. I'm looking forward to walking through the code in `stitch`. – Josh O'Brien Jan 05 '13 at 04:46
  • 9
    Just to add my 0.02, it is also easy to use redirection from the terminal. An example is `Rscript myfile.R > path/to/mylog.Rout` and instead of be printed to stdout (the screen), the output of the file is saved in your `.Rout` file. – James Pringle May 06 '14 at 15:19
  • 6
    To add to @JamesPringle, I often want my output to be both printed on a screen (to monitor in real time) and to a file (to look at later). I do `Rscript myfile.R | tee mylog.Rout` – Heisenberg Feb 11 '15 at 20:59
  • 1
    @Yihui 's awesome comment had to be fixed like that: `r Rscript -e "library(knitr); knitr::stitch(commandArgs(TRUE)[1])" my_script.R` – petermeissner Jul 30 '15 at 08:24
31

After trying the options described here, I found this post from Forester in r-bloggers . I think it is a clean option to consider.

I put his code here:

From command line

$ R CMD BATCH --no-save --no-restore '--args a=1 b=c(2,5,6)' test.R test.out &

Test.R

##First read in the arguments listed at the command line
args=(commandArgs(TRUE))

##args is now a list of character vectors
## First check to see if arguments are passed.
## Then cycle through each element of the list and evaluate the expressions.
if(length(args)==0){
    print("No arguments supplied.")
    ##supply default values
    a = 1
    b = c(1,1,1)
}else{
    for(i in 1:length(args)){
      eval(parse(text=args[[i]]))
    }
}

print(a*2)
print(b*3)

In test.out

> print(a*2)
[1] 2
> print(b*3)
[1]  6 15 18

Thanks to Forester!

Alex A.
  • 633
  • 9
  • 12
  • Important to note, if the arguments are of type character, no need to use single/double quotes. Eg: R CMD BATCH '--args a=FolderName' test.R test.out & – d2a2d Dec 01 '16 at 02:35
  • As mentioned in Forester's post, `--args` is the key. It also works with `R --no-save --no-restore --args a=1 < test.R` and `R --no-save --no-restore < test.R --args a=1` – Dave Mar 08 '17 at 17:24
  • Is there a way to pass arguments from the command line into the --args? So say we want to do a for loop in the command line, and then send it in the --args line. – user2809432 Apr 09 '19 at 22:33
  • @user2809432 did you manage to figure this out? I've got a similar problem – SpatialProgramming Oct 22 '20 at 15:40
  • @user2809432 I managed to sort it! Use `$ R` instead of `$ R CMD BATCH` I.e. for z in `seq $x $y`; do echo Running task $z R --file=code/simulate_urban_rural.R --args $z > output done – SpatialProgramming Oct 23 '20 at 08:33
12

You need to put arguments before my_script.R and use - on the arguments, e.g.

R CMD BATCH -blabla my_script.R

commandArgs() will receive -blabla as a character string in this case. See the help for details:

$ R CMD BATCH --help
Usage: R CMD BATCH [options] infile [outfile]

Run R non-interactively with input from infile and place output (stdout
and stderr) to another file.  If not given, the name of the output file
is the one of the input file, with a possible '.R' extension stripped,
and '.Rout' appended.

Options:
  -h, --help        print short help message and exit
  -v, --version     print version info and exit
  --no-timing           do not report the timings
  --            end processing of options

Further arguments starting with a '-' are considered as options as long
as '--' was not encountered, and are passed on to the R process, which
by default is started with '--restore --save --no-readline'.
See also help('BATCH') inside R.
Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • 2
    I notice if I do it this way and in the script use `args <- commandArgs(FALSE)` and then print args, I end up with of all arguments, including those which aren't mine, like `--restore`, `--save`, etc. If I use `commandArgs(TRUE)` I get no arguments at all. Is there a way to get just my own additional arguments? `--args` looks promising, but I haven't been able to get it to work... – Bryce Thomas Jan 05 '13 at 01:03
  • You have to count the arguments from the end (e.g. size-2, size-1, size) - yours will always be at the end. – ynka Oct 23 '14 at 20:57
9

In your R script, called test.R:

args <- commandArgs(trailingOnly = F)
myargument <- args[length(args)]
myargument <- sub("-","",myargument)
print(myargument)
q(save="no")

From the command line run:

R CMD BATCH -4 test.R

Your output file, test.Rout, will show that the argument 4 has been successfully passed to R:

cat test.Rout

> args <- commandArgs(trailingOnly = F)
> myargument <- args[length(args)]
> myargument <- sub("-","",myargument)
> print(myargument)
[1] "4"
> q(save="no")
> proc.time()
user  system elapsed 
0.222   0.022   0.236 
Richard Erickson
  • 2,568
  • 8
  • 26
  • 39
user1563570
  • 251
  • 1
  • 3
  • 6
4

I add an answer because I think a one line solution is always good! Atop of your myRscript.R file, add the following line:

eval(parse(text=paste(commandArgs(trailingOnly = TRUE), collapse=";")))

Then submit your script with something like:

R CMD BATCH [options] '--args arguments you want to supply' myRscript.R &

For example:

R CMD BATCH --vanilla '--args N=1 l=list(a=2, b="test") name="aname"' myscript.R &

Then:

> ls()
[1] "N"    "l"    "name"
micstr
  • 5,080
  • 8
  • 48
  • 76
ClementWalter
  • 4,814
  • 1
  • 32
  • 54
0

Here's another way to process command line args, using R CMD BATCH. My approach, which builds on an earlier answer here, lets you specify arguments at the command line and, in your R script, give some or all of them default values.

Here's an R file, which I name test.R:

defaults <- list(a=1, b=c(1,1,1)) ## default values of any arguments we might pass

## parse each command arg, loading it into global environment
for (arg in commandArgs(TRUE))
  eval(parse(text=arg))

## if any variable named in defaults doesn't exist, then create it
## with value from defaults
for (nm in names(defaults))
  assign(nm, mget(nm, ifnotfound=list(defaults[[nm]]))[[1]])

print(a)
print(b)

At the command line, if I type

R CMD BATCH --no-save --no-restore '--args a=2 b=c(2,5,6)' test.R

then within R we'll have a = 2 and b = c(2,5,6). But I could, say, omit b, and add in another argument c:

R CMD BATCH --no-save --no-restore '--args a=2 c="hello"' test.R

Then in R we'll have a = 2, b = c(1,1,1) (the default), and c = "hello".

Finally, for convenience we can wrap the R code in a function, as long as we're careful about the environment:

## defaults should be either NULL or a named list
parseCommandArgs <- function(defaults=NULL, envir=globalenv()) {
  for (arg in commandArgs(TRUE))
    eval(parse(text=arg), envir=envir)

  for (nm in names(defaults))
    assign(nm, mget(nm, ifnotfound=list(defaults[[nm]]), envir=envir)[[1]], pos=envir)
}

## example usage:
parseCommandArgs(list(a=1, b=c(1,1,1)))
Community
  • 1
  • 1
Dagremu
  • 325
  • 1
  • 7