5

I would like to use the evaluate package to simulate executing (lots of) r-scripts while recording the outputs using evaluate. Evaluate is designed to do exactly this and it works almost out of the box. However, when using Rscript, the user passes arguments via the command line --args which are retrieved in R using the base::commandArgs function.

Is there any sensible way I can override the value of --args from within a running R session such that an R script using base::commandArgs() would work as expected without having to modify the script itself?

Jeroen Ooms
  • 31,998
  • 35
  • 134
  • 207
  • Why not to set your args in your current session? ie Type in the console `args <- c(arge1,arg1,...)` – agstudy Jun 20 '13 at 07:38
  • I would like it to work for 3rd party r-scripts. – Jeroen Ooms Jun 20 '13 at 18:34
  • I don't get your point. What I mean you just comment one line in your script `#args <- commandArgs(TRUE)` then you define `args` in your console and you `source('script_name')`. – agstudy Jun 20 '13 at 18:53
  • 1
    Assume i have 10000000 scripts from other people that might or might not use `commandArgs` somewhere and I want to run them. – Jeroen Ooms Jun 20 '13 at 20:01
  • I can't assume anything. If you have 10000000 scripts to run You add this information to your question. And how do you will test arguments for 10000000 scripts. – agstudy Jun 20 '13 at 20:33

3 Answers3

11

Here is the @spacedman answer as a simple Rcpp-based implementation. We have to do some gymnastics on the on the vector to get it into char** format:

#include "Rcpp.h"
#include "R_ext/RStartup.h"

// [[Rcpp::export]]
void setCmdArgs(std::vector<std::string> x) {
  std::vector<char*> vec(x.size());
  for (unsigned int i=0; i<x.size(); i++) 
    vec[i] = const_cast<char*>(x[i].c_str());
  R_set_command_line_arguments(x.size(), static_cast<char**>(&(vec[0])));
}

/*** R
setCmdArgs(c("hello", "world"))
commandArgs()
setCmdArgs(c("good", "bye", "my", "friend"))
commandArgs()
*/

If you save this in a file and pull it in via a single call to sourceCpp(), then the R snippet at the bottom is executed too:

R> sourceCpp("/tmp/spacedman.cpp")

R> setCmdArgs(c("hello", "world"))

R> commandArgs()
[1] "hello" "world"

R> setCmdArgs(c("good", "bye", "my", "friend"))

R> commandArgs()
[1] "good"   "bye"    "my"     "friend"
R> 
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
7

I had a delve into R's guts and came up with some smelly intestines to play with.

The command line is copied from C's argc/argv into a global C variable with this function in the source code:

void R_set_command_line_arguments(int argc, char **argv)

So we need a little C wrapper to get round the fact that the first parameter isn't a pointer:

#include "R.h"
#include "R_ext/RStartup.h"
void set_command(int *pargc, char **argv){
  R_set_command_line_arguments(*pargc, argv);
}

compile in the usual way:

R CMD SHLIB setit.c

load, call:

> dyn.load("setit.so")
> commandArgs()
[1] "/usr/lib/R/bin/exec/R"
> invisible(.C("set_command",as.integer(2),c("Foo","Bar")))
> commandArgs()
[1] "Foo" "Bar"

from then on commandArgs() will return that vector until you change it.

Spacedman
  • 92,590
  • 12
  • 140
  • 224
1

At the top of your script you set commandArgs to be TRUE, if you don't pass anything on the command line the variable will be of length 0 so use an if statement to assign some values when you are not actually passing command line arguments. If you need to be careful about using default values you can set a flag to print a message when you use default values of command line args.

args <- commandArgs(TRUE)
argDefault <- FALSE
if( length(args) == 0 ){
   args <- whatever you want
   argDefault <- TRUE
}
res <- 2 * args[1]
if( argDefault )
      simpleWarning(message="No command args were passed: Use of default values")  
agstudy
  • 119,832
  • 17
  • 199
  • 261
Simon O'Hanlon
  • 58,647
  • 14
  • 142
  • 184