Is there a way to debug an R script run from the command line with Rscript.exe - r

Is it possible to debug an R source file which is executed with Rscript.exe?
> Rscript.exe mysource.R various parameters
Ideally, I would like to be able to set a break-point somewhere in the mysource.R file in RStudio.
Is entering the R debugger directly at the command line possible for instance by adding some debug directive to the source file?
Maybe sourcing the file from R would work? How? How do I pass the command line arguments "various parameters" so that commandArgs() returns the correct values?
The mysource.R could look as follows (in practice it is much more complicated).
#!/usr/bin/Rscript
args <- commandArgs(trailingOnly=TRUE)
print(args)

As far as debugging from console is concerned there are few questions related to it without an answer.
Is there a way to debug an RScript call in RStudio? and Rscript debug using command line
So, I am not sure if something has changed and if it is now possible.
However, we can debug from sourcing the file by using a hack. You could add browser() in the file wherever you want to debug. Consider your main file as :
main.R
args <- commandArgs(trailingOnly=TRUE)
browser()
print(args)
Now, we can override the commandArgs function and pass whatever arguments we want to pass which will be passed when you source the file.
calling_file.R
commandArgs <- function(...) list(7:9, letters[1:3])
source("main.R")
After running the source command, you could debug from there
Called from: eval(ei, envir)
#Browse[1]> args
#[[1]]
#[1] 7 8 9
#[[2]]
#[1] "a" "b" "c"

There's no native way to debug Rscript in the command line, but you can use a kind of hacky workaround I whipped up with readLines and eval.
ipdb.r <- function(){
input <- ""
while(!input %in% c("c","cont","continue"))
{
cat("ipdb.r>")
# stdin connection to work outside interactive session
input <- readLines("stdin",n=1)
if(!input %in% c("c","cont","continue","exit"))
{
# parent.frame() runs command outside function environment
print(eval(parse(text=input),parent.frame()))
}else if(input=="exit")
{
stop("Exiting from ipdb.r...")
}
}
}
Example usage in an R file to be called with Rscript:
ipdbrtest.R
a <- 3
print(a)
ipdb.r()
print(a)
Command line:
$ Rscript ipdbrtest.R
[1] 3
ipdb.r>a+3
[1] 6
ipdb.r>a+4
[1] 7
ipdb.r>a <- 4
[1] 4
ipdb.r>c
[1] 4
If you're considering using R instead of Rscript, you could pass it environment variables and retrieve them with Sys.getenv().

Related

RStudio local job: "source" multiple scripts using "sapply" will return nothing

I have three scripts in the same directory: main.R, func1.R, func2.R. The codes are
main.R:
rm(list = ls())
x <- 0
filelist <- c("func2.R", "func1.R")
print(ls())
sapply(filelist, source)
print(ls())
func1.R:
x1 <- 1
func2.R:
x2 <- 2
If I run main.R in RStudio, the output will be
[1] "filelist" "x"
[1] "filelist" "x" "x1" "x2"
This means the results of func1.R and func2.R are exported into the global environment. However, if I submit main.R as a local job in RStudio, the output will be
[1] "filelist" "x"
[1] "filelist" "x"
I know I can solve this by using loop to source each script separately. I'm simply curious the reason why the sapply function behaves differently in console and local job, and how to make it work if I insist using sapply to source all scripts together? Thanks.
I think there is a misunderstanding of what is meant by local job. The feature run local job is quite a new feature from RStudio and lets the user start a job and run it local somewhere so that it is not inferring with the users own session. The main Idee here is that if you have some computationally heavy job you start this on some other maschine and then collect the results when they are done. If you want the scripts to run in your own session you should use the classic source command

Interactive Debugging in R

I have a toy R script (test.R) as follows:
myString <- "Hello World"
browser()
print(myString)
I want to interactively debug the script, hence the 'browser()' call.
However, when I run the script like this:
Rscript test.R
I just see:
Called from: top level
[1] "Hello World"
i.e. the 'browser()' call seems to get skipped. When I run the script like this:
R -f test.R
I see:
>
> myString <- "Hello World"
> browser()
Called from: top level
Browse[1]> print (myString)
[1] "Hello World"
Browse[1]>
>
But, execution does not stop at 'Browse[1]>' to allow me to debug.
What am I missing here? In python, I can insert "import pdb; pdb.set_trace()" to debug. I am looking for something similar in R.
See this for how to use browser(). You will be able to source('scriptname.R') a script in the R interactive environment on terminal (by running R instead of Rscript scriptname.R) and then browser() will behave very similar to import pdb; pdb.set_trace() . The one major caveat is arguments - you could manually configure that to use the arguments you would supply on the command line by using say if(interactive()) (or there might be other ways to achieve this).

Running R function with arguments in 32 bit R, inside 64 bit R

Suppose I want to run the function
test.function <- function(arg1){
print(arg1)
}
How can I run, lets say:
test.function("Hello world")
in 32 bit mode, using 64 bit R? I have managed running an entire script in 32 bit mode using
system(paste0(Sys.getenv("R_HOME"), "/bin/i386/Rscript.exe ",'"some_script.R"'))
But how can I change this, so that it can run a function with arguments, instead of an entire script?
EDIT
Following the answer by Roman Luštrik and running
system('Rscript test.script.R "hello"')
Gives me the following error:
Error in winDialog(type = "ok", message = message_text) :
winDialog() cannot be used non-interactively
call: -> check.for.updates.R -> winDialog
Running stopped
Warning message:
running command 'Rscript test.script.R "hello"' had status 1
(The error message was in my native language, so I had to translate a few words, so the text might differ slightly on other systems)
You can't run a specific function only, you will have to create a script. This does not stop you from creating a one-function only script, though.
If you create a script called test.script.R and put it somewhere where you can find it.
args <- commandArgs(trailingOnly = TRUE)
str(args)
test.function <- function(x) {
message("Your input:")
message(x)
}
invisible(sapply(args, test.function))
Open up a terminal window. You can use Windows' cmd.exe (press Windows key and type cmd.exe or Command Prompt or whatever you have in your perhaps localized version of the system). Navigate to where the script is located and run it using the below command.
$ Rscript test.script.R "hello" "hello" "won't you tell me your name" "i hate the doors"
chr [1:4] "hello" "hello" "won't you tell me your name" ...
Your input:
hello
Your input:
hello
Your input:
won't you tell me your name
Your input:
i hate the doors
Or, you could use system to do the same thing through R.
system('Rscript test.script.R "hello" "hello" "won't you tell me your name" "i hate the doors"')
Notice the way I use single and double quotes. Single quotes are on the outer side. This call also assumes the script is located in the workspace where R is currently looking. You can change that using setwd(), though.
I managed to find a solution myself, by modifying Roman Luštrik's solution.
Following his example we have the script called test_script.R:
args <- commandArgs(trailingOnly = TRUE)
test.function <- function(x) {
print(x)
}
args.run <- list(x = args)
mydata <- do.call(test.function, args = args.run)
save(mydata, file = "Data.Rda") # If you need the output to an R object
Then in another script that runs 64 bit R, we can run this function in 32 bit R by:
pathIn32BitRScript <- '"C:/Some path/test_script.R"'
system(paste0(Sys.getenv("R_HOME"), "/bin/i386/Rscript.exe", pathIn32BitRScript, " ", "Hello world") )
load("Data.Rda") # Loads the results into an object called mydata
invisible(file.remove("Data.Rda")) # Deletes the file we created
In this example we have x = "Hello World". In case you have spaces in your path you will need the double quotes as I have in this example.

R Script as a Function

I have a long script that involves data manipulation and estimation. I have it setup to use a set of parameters, though I would like to be able to run this script multiple times with different sets of inputs kind of like a function.
Running the script produces plots and saves estimates to a csv, I am not particularly concerned with the objects it creates.
I would rather not wrap the script in a function as it is meant to be used interactively.
How do people go about doing something like this?
I found this for command line arguments : How to pass command-line arguments when source() an R file but still doesn't solve the interactive problem
I have dealt with something similar before. Below is the solution I came up with.
I basically use list2env to push variables to either the global or function's local environment
and I then source the function in the designated environment.
This can be quite useful especially when coupled with exists as shown in the example below which would allow you to keep your script stand-alone.
These two questions may also be of help:
Source-ing an .R script within a function and passing a variable through (RODBC)
How to pass command-line arguments when source() an R file
# Function ----------------------------------------------------------------
subroutine <- function(file, param = list(), local = TRUE, ...) {
list2env(param, envir = if (local) environment() else globalenv())
source(file, local = local, ...)
}
# Example -----------------------------------------------------------------
# Create an example script
tmp <- "test_subroutine.R"
cat("if (!exists('msg')) msg <- 'no argument provided'; print(msg)", file = tmp)
# Example of using exists in the script to keep it stand-alone
subroutine(tmp)
# Evaluate in functions environment
subroutine(tmp, list(msg = "use function's environment"), local = TRUE)
exists("msg", envir = globalenv()) # FALSE
# Evaluate in global environment
subroutine(tmp, list(msg = "use global environment"), local = FALSE)
exists("msg", envir = globalenv()) # TRUE
unlink(tmp)
Just to clarify what was alluded to in Hansi's comment, here is one approach to this issue:
Wrap the script into a function, since this will let you go up one level of abstraction if needed, and will also make it easier to call the function whenever it is needed in any other script.
In cases where you want to use the script interactively, you can put a browser() call somewhere in your script. At the point where browser() is called, the function will pause and keep the environment as-is within the function, and you can then step through the function and use R interactively from within the function.
In the base package, check ?commandArgs, you can use this to parse out arguments from the command line.
If I have a script, test.R, containing the code:
args <- commandArgs(trailingOnly=TRUE)
for (arg in args){
print(arg)
}
and I call it from the command line with rscript as follows:
rscript test.R arg1 arg2 arg3
The output is:
[1] "arg1"
[1] "arg2"
[1] "arg3"

Write access to commandArgs?

I know that I can use commandArgs to read the command line arguments passed to a script in R, but I would like to debug a command line script by sourceing it in R and making it run using custom command line arguments. Is there a way of modifying the command line arguments without modifying the script file?
My scripts are normally using the optparse package for actual argument parsing, if that helps.
I'll try and expand what I said in a comment.
The python way of writing scripts usually involves detecting if the file is being run as a script, handling the args, and then calling functions defined in the file. Something like:
def foo(x):
return x*2
if __name__=="__main__":
v = sys.argv[1]
print foo(v)
This has the advantage that you can import the file into an interactive python session and the code in the 'if' block doesn't run. You can then test the foo function interactively.
Now is there a way you can check in R if the file is being run as a script, or being sourced from an interactive session?
foo=function(x){
return(x*2)
}
if(!interactive()){
x = as.numeric(commandArgs(trailingOnly=TRUE)[1])
print(foo(x))
}
If run with Rscript argtest.R 22 will print 44, if you run R interactively and do source("argtest.R") it won't run the code in the if block. Its a nice pattern.
How about simply overwriting it with your own definition, e.g.
commandArgs <- function(trailingOnly=FALSE) {
args<- c("/foo/bar", "baz")
# copied from base:::commandArgs
if (trailingOnly) {
m <- match("--args", args, 0L)
if (m)
args[-seq_len(m)]
else character()
}
else args
}
The simplest solution is to replace source() with system(). Try
system("Rscript file_to_source.R 1 2 3")

Resources