27

I've looked through many questions similar to this (see end of post), but I haven't found any solutions that actually accomplish what I need. I code on either Windows or Fedora depending on the project, and I code for people who use Windows and several Linux distributions.

Part of my job is making R scripts for people that automatically analyze data and create graphs. Most commonly, I'll just send them the script and it will generate the graph. This way, if the data changes or expands, I don't need to re-run the script for them (also, they can make changes as needed).

The problem is that I don't know how to get an R-script to find out where itself is located. It would be very convenient to just be able to create code that works as follows:

  1. User saves script to folder containing data, then runs script.
    • I usually just email the script to the person I'm working with.
    • They save the script to a folder containing the data they want analyzed/graphed.
    • Ideally, they would just launch R, load the script, then run the script.
  2. Script determines its own location, then sets that as the working directory.
  3. Script analyzes data inside its own directory.
  4. Script generates graph(s) and saves it to its own directory.

This question only deals with Step 2. Everything else flows nicely as long as I can accomplish that. It would be nice to have something such as:

setwd(FindThisScriptsLocation())

The line: source(..., chdir = T) has been suggested here, but it can't be used for a script to reference itself unless it knew its own path.

Here are some related questions:

Community
  • 1
  • 1
Will Beason
  • 3,417
  • 2
  • 28
  • 46
  • 2
    Please provide links to the similar questions, and tell us what specifically about them does not work in this case. – Joshua Ulrich Aug 01 '13 at 17:30
  • 4
    In step 1, how is the user running the script? That is where that information can get passed to the function. – Ricardo Saporta Aug 01 '13 at 17:30
  • Seems it would be much more sensible for your script _not_ to meddle with the user's working directory. It should just save anything it produces in the location where the user is working. – Hong Ooi Aug 01 '13 at 17:33
  • 1
    Related: [Getting path of an R script](http://stackoverflow.com/q/3452086/271616), [get filename and path of `source`d file](http://stackoverflow.com/q/8835426/271616) – Joshua Ulrich Aug 01 '13 at 17:43
  • 1
    @HongOoi The people I work with have expressed that they specifically want me to do this. Currently, we have to manually set the working directory every time it changes hands or location. – Will Beason Aug 01 '13 at 17:57
  • 1
    This seems overly-complicated. Why not just send them a workspace file (created via `save.image`) that contains a function they can call? As long as the `.RData` files are associated with R, they can just double-click the file after saving it in whatever directory, then call the function. – Joshua Ulrich Aug 01 '13 at 18:21
  • @RicardoSaporta Usually the users start R, load the script, then run the script. How can the location information be passed to the functions in the script? – Will Beason Aug 01 '13 at 19:39
  • @JoshuaUlrich While that does work for static data sets, sometimes the data sets I work with expand (say we collect more data) or contracts (or say we find out that data wasn't recorded correctly). Also, the solution doesn't work for when I have the script output graphs: they will be dumped into whatever the user's current working directory is rather than the directory of the data files. – Will Beason Aug 01 '13 at 19:46
  • @beason4251 what does "load the script" mean? What does "run the script" mean? Is it run from within R; is `source()` involved? Is it run from the command line? – GSee Aug 01 '13 at 19:47
  • Did you try it? It works for whatever data set is in the directory with the workspace file you send them. The graphs will also be saved to the directory containing the workspace file. This is because the working directory is set to the directory containing the `.RData` file when you double-click it. – Joshua Ulrich Aug 01 '13 at 19:51
  • @GSee The people who use my scripts use the RGui that comes with R, not through the command line. The "load the script" by using File->Open script. They "run the script" by pressing Crtl+R with the text of the script selected. – Will Beason Aug 01 '13 at 21:08
  • @JoshuaUlrich Ah, I misunderstood. I thought you were saying the data would be in the workspace, not just the functions. That does what I need. So I just have to put everything into a wrapper function an that will handle it. Thanks! – Will Beason Aug 01 '13 at 21:09
  • For a false sense of hope, check out `?getSrcFilename` and/or `?srcfile`. These functions will tell you the name of the file that contains a function that you've already sourced. Of course if you've sourced it, then you already know the location. – geneorama Jun 06 '16 at 20:37

5 Answers5

14

Had the same problem, here's what I came up with. It works with source() and rmarkdwon::render() on windows and on linux.

Update: the function get_scriptpath() is now available as part of my envDocument package on CRAN. See https://cran.r-project.org/package=envDocument

#' Get the path of the calling script
#' 
#' \code{get_scriptpath} returns the full path of the script that called this function (if any)
#' or NULL if path is not available
#' 
#' @examples 
#' mypath <- get_scriptpath()
#' @export
#' 
get_scriptpath <- function() {
  # location of script can depend on how it was invoked:
  # source() and knit() put it in sys.calls()
  path <- NULL

  if(!is.null(sys.calls())) {
    # get name of script - hope this is consisitent!
    path <- as.character(sys.call(1))[2] 
    # make sure we got a file that ends in .R, .Rmd or .Rnw
    if (grepl("..+\\.[R|Rmd|Rnw]", path, perl=TRUE, ignore.case = TRUE) )  {
      return(path)
    } else { 
      message("Obtained value for path does not end with .R, .Rmd or .Rnw: ", path)
    }
  } else{
    # Rscript and R -f put it in commandArgs
    args <- commandArgs(trailingOnly = FALSE)
  }
  return(path)
}
DonJ
  • 923
  • 11
  • 18
  • This is the only of the proposed answers that works for me. Running Lubuntu 16.04. – emilBeBri Mar 12 '17 at 23:01
  • 1
    I am confused about how to use this function..what is the argument I pass to it? When calling get_scriptpath() with no arguments I get error: `Error in get_scriptpath() : No path information available` – Asy Jan 31 '18 at 21:05
  • 1
    There are no arguments to this function. It will ONLY work when called from a running script (e.g., if you run a script or RMD file using Rscript, source, rmarkdown::render). If you call it from the R console, it won't work because the console isn't a script. The envDocument package at https://cran.r-project.org/package=envDocument provides a version of this function with improved documentation and error handling. – DonJ Feb 02 '18 at 18:28
  • Why filter on file extension? In what cases have you encountered a calling file that did not have one of the anticipated extensions? – bokov Sep 06 '19 at 11:56
  • 1
    Paranoia - if for some reason the system call to run the script/report isn't where I expect it then I wanted to get a heads up; I'm trusting that it will always be in the same position in the sys.call return value. in retrospect I might even escalate this to a warning or error. And I don't want to imagine if for some reason there's an incorrect value that has an expected extension... – DonJ Sep 09 '19 at 14:55
  • Just to emphasize this has to be used when executing a script or a full run of knitr. Just executing it from a chunk in a notebook or R markdown doc will not do. – Pablo Adames Nov 18 '20 at 14:10
  • Agree - the point of this is to document the call within a script or rmarkdown::rendr. For use from the console there is no 'script' so the concept does not apply. – DonJ May 05 '21 at 14:00
5

somewhere in the "load the script" process, you are passing the name and path of the R script. I'm suggesting to capture that information and then use a wrapper script to execute your main script.

Option 1 (programatically)

the wrapper function that takes as an argument the path and fiile name of the script to execute

FILE <- "~/Desktop/myFolder/InHere/myScript.R"

Option 2 (interactively)

at the start of your wrapper function, let the user click through to the file:

FILE <- file.choose()

THEN:

DIR  <- dirname(FILE)

and there you have your directory/folder and you can execute your script as normal passing DIR as a parameter

Ricardo Saporta
  • 54,400
  • 17
  • 144
  • 178
3

Hey I have a possible solution that's a little bit of extra initial work, but should be able to do what you need.

First make your R script take in a parameter which will be the location of the script. Next you just need to create a Bash/Batch (one each for windows and unix) that will

1) get its own directory

2) search the directory for an R script (simple *.R search)

3) call the R script with it's own directory from step 1.

Then you simply package the Bash and Batch scripts with the folder you give to your clients and ask them to just run the relevant script for their environment.

You should, in theory, only have to create the Bash/Batch scripts once.

EDIT: I've created a simple bash script that works for this problem, see below

#!/bin/bash

#Modify the search string to narrow the search
SEARCH_STRING="*.R"

#Get the current directory
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo ${DIR}

#Get the name of the R script
R_SCRIPT=`find $DIR -name ${SEARCH_STRING} -type f -exec basename {} \;`
echo ${R_SCRIPT}

Rscript ${R_SCRIPT} ${DIR}

I'm not so well versed in the Windows Shell so you'll have to do that one yourself :P

You can test it with this R script;

options(echo = FALSE) #So we don't get the script echo'd

arguments <- commandArgs(trailingOnly = TRUE) #getting the arguments

working_directory <- arguments[1]

setwd(working_directory)

getwd() #print out to test
Darragh.McL
  • 125
  • 1
  • 10
1

I assume this is Windows.

Following up Ricardo's suggestion: Have the client's systems set up in a way that, if a script is double-clicked, the R interpreter is started in the directory of the script. You could also assign a special extension for this behavior (say, .Rwd for "R script setting the work directory"). Then, you don't need to setwd() within the script.

For starters, the following command line script might do (untested):

pushd %~d1%~p1
R --vanilla < "%1"

Associate .Rwd files with this script.

If you need to source() other scripts, consider using the chdir=T argument.

krlmlr
  • 25,056
  • 14
  • 120
  • 217
  • I mostly use Windows, but I do also use Fedora depending on the project. I write code for people who run Windows and a few Linux distributions. Your answer definitely will work (I'll learn how to do what you said, it sounds very useful), but it isn't completely ideal. – Will Beason Aug 01 '13 at 19:28
  • 4
    You can abbreviate `%~d1%~p1` as `%~dp1` – Matthew Lundberg Aug 01 '13 at 20:10
-2

If you're using RStudio, simply hold the cursor over the script and the path will appear

enter image description here

stevec
  • 41,291
  • 27
  • 223
  • 311
  • @stevec- the question related to batch execution not to use of the Rstudio GUI – DonJ May 05 '21 at 14:02
  • @DonJ that may be true, but I arrived here searching for how to view the location of an R file in RStudio, so I'd guess others will end up here from the same google search. Hence it's pretty harmless to include this info here – stevec May 05 '21 at 14:11