55

I would like to set the working directory to the path of current script programmatically but first I need to get the path of current script.

So I would like to be able to do:

current_path = ...retrieve the path of current script ...
setwd(current_path) 

Just like the RStudio menu does: RStudio set working directory

So far I tried:

initial.options <- commandArgs(trailingOnly = FALSE)
file.arg.name <- "--file="
script.name <- sub(file.arg.name, "", initial.options[grep(file.arg.name, initial.options)])
script.basename <- dirname(script.name)

script.name returns NULL

source("script.R", chdir = TRUE)

Returns: Error in file(filename, "r", encoding = encoding) : cannot open the connection In addition: Warning message: In file(filename, "r", encoding = encoding) : cannot open file '/script.R': No such file or directory

dirname(parent.frame(2)$ofile)

Returns: Error in dirname(parent.frame(2)$ofile) : a character vector argument expected ...because parent.frame is null

frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])

Returns: Null because frame_files is a list of 0

thisFile <- function() {
    cmdArgs <- commandArgs(trailingOnly = FALSE)
    needle <- "--file="
    match <- grep(needle, cmdArgs)
    if (length(match) > 0) {
        # Rscript
        return(normalizePath(sub(needle, "", cmdArgs[match])))
    } else {
        # 'source'd via R console
        return(normalizePath(sys.frames()[[1]]$ofile))
    }
}

Returns: Error in path.expand(path) : invalid 'path' argument

Also I saw all answers from here, here, here and here. No joy.

Working with RStudio 1.1.383

EDIT: It would be great if there was no need for an external library to achieve this.

Panos Kal.
  • 12,172
  • 8
  • 64
  • 77
  • Probably `source("/script.R", chdir = TRUE)` will work as the help file says about chdir: * if TRUE and file is a pathname, the R working directory is temporarily changed to the directory containing file for evaluating.* The error message you get says that R can't find a file named script.R in the current working directory. – lmo Oct 31 '17 at 20:39
  • My file name is lpp.R and I do give this name... I remember in other versions of RStudio I did it easily with parent frame, now nothing – Panos Kal. Oct 31 '17 at 20:42
  • In an R studio project folder, it is a good practice to assume that all file paths are relative to the project's root directory. See [Stop the working directory insanity](https://gist.github.com/jennybc/362f52446fe1ebc4c49f). I personally do not use it but the [here](https://krlmlr.github.io/here/) package is recommended to find where a given file is located. – Paul Rougieux Oct 31 '17 at 23:01
  • @Imo -- that only works if you source the file, but not if you call it with Rscript. – abalter Aug 01 '19 at 21:07
  • 1
    @PaulRougieux -- that only works if the file you are running (or sourcing) is in the project directory. If you are running (or sourcing) a file in another location, it will not work. – abalter Aug 01 '19 at 21:08

10 Answers10

102

In RStudio, you can get the path to the file currently shown in the source pane using

rstudioapi::getSourceEditorContext()$path

If you only want the directory, use

dirname(rstudioapi::getSourceEditorContext()$path)

If you want the name of the file that's been run by source(filename), that's a little harder. You need to look for the variable srcfile somewhere back in the stack. How far back depends on how you write things, but it's around 4 steps back: for example,

fi <- tempfile()
writeLines("f()", fi)
f <- function() print(sys.frame(-4)$srcfile)
source(fi)
fi

should print the same thing on the last two lines.

user2554330
  • 30,741
  • 4
  • 34
  • 76
  • This works great. The good thing is that it has fewer dependencies vs `here` package. The truth is that I was hoping to do this without a library. I will wait one/two days to check if someone has something to add and then I will accept it. Thanks. – Panos Kal. Nov 01 '17 at 04:45
  • I just did a clean install in another PC in the office. Newest R Studio (`1.1.383`), newest R version (`R version 3.4.2 (2017-09-28)`). Now I get this error: `Error: 'getSourceEditorContext' is not an exported object from 'namespace:rstudioapi'` – Panos Kal. Nov 01 '17 at 15:29
  • What version of `rstudioapi` are you using? 0.7 is current on CRAN, but it's fairly recent. – user2554330 Nov 01 '17 at 19:02
  • Thanks. The other PC grabbed version `0.5` from CRAN and I had to manually update the package. Cant guess why it picked `0.5`... – Panos Kal. Nov 02 '17 at 13:21
  • Perhaps an out of date mirror. – user2554330 Nov 02 '17 at 17:25
  • @PanosKal., you probably should rethink about why you need to get the full path of the current script. Jennybc's point in [Stop the working directory insanity](https://gist.github.com/jennybc/362f52446fe1ebc4c49f) is that all local data loaded in the script should use paths that are *relative* to the project home directory. Then any script you code, assumes that the working directory is always set to the base path of the project. By default R Studio will always `setwd()` to the project home directory when you load a project. – Paul Rougieux Nov 08 '17 at 11:45
  • 1
    I need to make the code portable, having all data in a specific folder and make it work as fast as possible in other people pc. Specially those who just know how to press the play button and see the code producing results without any user interference – Panos Kal. Nov 08 '17 at 11:56
  • 2
    This does not run in terminal like `Rscript test.R` – Onur Demir Sep 09 '20 at 14:50
  • @OnurDemir: No, to get the name of a script run through `Rscript`, you need to look at `commandArgs()`. – user2554330 Sep 09 '20 at 20:20
21

Update March 2019

Based on Alexis Lucattini and user2554330 answers, to make it work on both command line and RStudio. Also solving the "as_tibble" deprecated message

library(tidyverse)
getCurrentFileLocation <-  function()
{
    this_file <- commandArgs() %>% 
    tibble::enframe(name = NULL) %>%
    tidyr::separate(col=value, into=c("key", "value"), sep="=", fill='right') %>%
    dplyr::filter(key == "--file") %>%
    dplyr::pull(value)
    if (length(this_file)==0)
    {
      this_file <- rstudioapi::getSourceEditorContext()$path
    }
    return(dirname(this_file))
}
Juan Bernabe
  • 211
  • 2
  • 2
17

Update August 2018

TLDR: The here package helps you build a path from the project's root folder no matter where in the folder hiearchy the R scripts or Rmd documents are stored.

The here package remains available on CRAN. The development version has been moved to github.com/r-lib/here. The points mentioned in the sites quoted below remain valid.

Previous answer

Read the Ode to the here package:

Do you: Have setwd() in your scripts? PLEASE STOP DOING THAT. This makes your script very fragile, hard-wired to exactly one time and place. As soon as you rename or move directories, it breaks. Or maybe you get a new computer? Or maybe someone else needs to run your code?

[...]

Classic problem presentation: Awkwardness around building paths and/or setting working directory in projects with subdirectories. Especially if you use R Markdown and knitr, which trips up alot of people with its default behavior of “working directory = directory where this file lives”. [...]

Install the here package:

install.packages("here")
library(here)
here()
here("construct","a","path")

Documentation of the here() function:

Starting with the current working directory during package load time, here will walk the directory hierarchy upwards until it finds a directory that satisfies at least one of the following conditions:

  • contains a file matching [.]Rproj$ with contents matching ^Version: in the first line
  • [... other options ...]
  • contains a directory .git

Once established, the root directory doesn't change during the active R session. here() then appends the arguments to the root directory.

The development version of the here package is available on github.

Paul Rougieux
  • 8,881
  • 3
  • 56
  • 95
  • 3
    This only works if you are `source`ing or `Rscript`ing a file in the same project tree. If you are calling a utility script for instance that lives somewhere else, this does not work. – abalter Aug 01 '19 at 21:14
  • 2
    Let's say that R is not made for actual reliable development. It is unbelievably hard to import relative to a given file path if you have multiple packages relying on each other. Your only option is to import everything from the root script globally. This is unacceptable for reliable development. – Stefano Borini Sep 09 '19 at 13:38
  • @StefanoBorini R is made to explore and analyze statistics. You probably want to group all your data loading operations at the beginning loading data in one place from a file or a database, then passing data frames to other packages. – Paul Rougieux Sep 10 '19 at 07:26
  • @abalter As long as you can clearly specify a path once as a variable `data_path = "my_data_folder"` and make all other file references relative to that variable: `file_one = file.path(data_path,"file_one.csv")` you should be OK. This kind of pattern would be the same in any other language. – Paul Rougieux Sep 10 '19 at 07:32
  • 3
    @PaulRougieux it's not about loading data. It's about importing other R files. e.g. having to use source(). compared to python import it's tragically bad, because you have to use very bad tricks to ensure a proper reliable import. This is very important when you have to write code that is production ready and thus have some degree of control. – Stefano Borini Sep 10 '19 at 12:04
  • @StefanoBorini I tend to put production code inside an R package. Creating an R package is rather straightforward with Hadley Whicham's [book on R packages](http://r-pkgs.had.co.nz/). In fact this is something I wish more colleagues would do, I'm in a environmental modeling environment where there are a lot of R users. – Paul Rougieux Sep 10 '19 at 15:00
  • 1
    @PaulRougieux it is also not a matter of package. You can't have nested hierarchies or have file A that imports file B _within the same codebase_ (e.g. between pieces of code in different files that are part of the same package). – Stefano Borini Sep 10 '19 at 15:31
  • 2
    That's not how an R package works, you have a package name space and functions call each other within that name space. – Paul Rougieux Sep 10 '19 at 17:20
  • In R, sourcing a file is different from loading a library. Sourcing a file that contains R code implies parsing text into an expression object that will be evaluated in the current environment. Loading a library In R, implies binding objects that are real "closures" because they have an enclosing environment, where they were defined, and that is immutable. – Pablo Adames Jul 19 '21 at 03:10
7

If you're running an Rscript through the command-line etc

Rscript /path/to/script.R

The function below will assign this_file to /path/to/script

library(tidyverse)
get_this_file <- function(){
    commandArgs() %>% 
       tibble::enframe(name=NULL) %>%
       tidyr::separate(col=value, into=c("key", "value"), sep="=", fill='right') %>%
       dplyr::filter(key == "--file") %>%
       dplyr::pull(value)
}
this_file <- get_this_file()
Alexis Lucattini
  • 1,001
  • 8
  • 12
6

Here is a custom function to obtain the path of a file in R, RStudio, or from an Rscript:

stub <- function() {}
thisPath <- function() {
  cmdArgs <- commandArgs(trailingOnly = FALSE)
  if (length(grep("^-f$", cmdArgs)) > 0) {
    # R console option
    normalizePath(dirname(cmdArgs[grep("^-f", cmdArgs) + 1]))[1]
  } else if (length(grep("^--file=", cmdArgs)) > 0) {
    # Rscript/R console option
    scriptPath <- normalizePath(dirname(sub("^--file=", "", cmdArgs[grep("^--file=", cmdArgs)])))[1]
  } else if (Sys.getenv("RSTUDIO") == "1") {
    # RStudio
    dirname(rstudioapi::getSourceEditorContext()$path)
  } else if (is.null(attr(stub, "srcref")) == FALSE) {
    # 'source'd via R console
    dirname(normalizePath(attr(attr(stub, "srcref"), "srcfile")$filename))
  } else {
    stop("Cannot find file path")
  }
}

https://gist.github.com/jasonsychau/ff6bc78a33bf3fd1c6bd4fa78bbf42e7

Jason Chau
  • 61
  • 1
  • 2
4

Another option to get current script path is funr::get_script_path() and you don't need run your script using RStudio.

  • What is `funr`? – abalter Aug 01 '19 at 21:10
  • it's a package, you can find more information about it [here](https://cran.r-project.org/web/packages/funr/index.html). In R, even if you don't attach a package in the current working environment, you can use their function by the following way `package::function()` – Manuel Sánchez Mendoza Aug 09 '19 at 21:37
  • 2
    Let's say you have a script located in `/path/to/project/script.R` and inside that script you have a statement `funr::get_script_path()`, it will return a value of `/path/to/project`. Note: It is returning the **full directory path** of the current file and **not the full path** of the current file which should supposedlty `/path/to/project/script.R`. The function worked for my case though. – Abel Callejo Nov 06 '20 at 09:02
1

I had trouble with all of these because they rely on libraries that I couldn't use (because of packrat) until after setting the working directory (which was why I needed to get the path to begin with).

So, here's an approach that just uses base R. (EDITED to handle windows \ characters in addition to / in paths)

args = commandArgs()

scriptName = args[substr(args,1,7) == '--file=']

if (length(scriptName) == 0) {
  scriptName <- rstudioapi::getSourceEditorContext()$path
} else {
  scriptName <- substr(scriptName, 8, nchar(scriptName))
}

pathName = substr(
  scriptName, 
  1, 
  nchar(scriptName) - nchar(strsplit(scriptName, '.*[/|\\]')[[1]][2])
)

Dov Rosenberg
  • 495
  • 3
  • 16
1

The following solves the problem for three cases: RStudio source Button, RStudio R console (source(...), if the file is still in the Source pane) or the OS console via Rscript:

this_file = gsub("--file=", "", commandArgs()[grepl("--file", commandArgs())])
if (length(this_file) > 0){
  wd <- paste(head(strsplit(this_file, '[/|\\]')[[1]], -1), collapse = .Platform$file.sep)
}else{
  wd <- dirname(rstudioapi::getSourceEditorContext()$path)
}

print(wd)
shosaco
  • 5,445
  • 1
  • 22
  • 45
1

The following code gives the directory of the running Rscript if you are running it either from Rstudio or from the command line using Rscript command:

if (rstudioapi::isAvailable()) {
  if (require('rstudioapi') != TRUE) {
    install.packages('rstudioapi')
  }else{
    library(rstudioapi) # load it
  }
 wdir <- dirname(getActiveDocumentContext()$path)
}else{
 wdir <- getwd()
}

setwd(wdir)
0

If you don't want to use (or have to remember) code, simply hover over the script and the path will appear

enter image description here

stevec
  • 27,285
  • 13
  • 133
  • 181