Question

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?

Was it helpful?

Solution 2

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.

OTHER TIPS

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> 

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")  
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top