66

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.

M--
  • 25,431
  • 8
  • 61
  • 93
Panos Kalatzantonakis
  • 12,525
  • 8
  • 64
  • 85
  • 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 Kalatzantonakis 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

119

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
  • 37,248
  • 4
  • 43
  • 90
  • 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 Kalatzantonakis 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 Kalatzantonakis 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 Kalatzantonakis 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 Kalatzantonakis 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
24

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
  • 241
  • 2
  • 2
  • 2
    This actually works and should be the accepted answer in all related questions. – abalter Aug 01 '19 at 21:13
  • 1
    Nice. Within `if (length ...) { }`, below `this_file <- ...` I added `if(this_file=="") message("You are running code in RStudio in a new R script window, so the filename is blank.")` – user3799203 Sep 15 '21 at 00:15
  • Great answer ! It's also very informative of the current way of programming R, that uses tidyverse pipelines instead of loops. – Hilton Fernandes Apr 18 '22 at 15:18
20

TLDR: The here package (available on CRAN) helps you build a path from a project's root directory. R projects configured with here() can be shared with colleagues working on different laptops or servers and paths built relative to the project's root directory will still work. The development version is at github.com/r-lib/here.

With git

You certainly store your R code in a directory. This directory is probably part of a git repository and/or an R studio project. I would recommend building all paths relative to that project's root directory. For example let's say that you have an R script that creates reusable plotting functions and that you have an R markdown notebook that loads that script and plots graphs in a nice (so nice) document. The project tree would look something like this

├── notebooks
│   ├── analysis.Rmd
├── R
│   ├── prepare_data.R
│   ├── prepare_figures.R

From the analysis.Rmd notebook, you would import plotting function with here() as such:

source(file.path(here::here("R"), "prepare_figures.R"))

Why?

Hadley Wickham in a Stackoverflow comment:

"You should never use setwd() in R code - it basically defeats the idea of using a working directory because you can no longer easily move your code between computers. – hadley Nov 20 '10 at 23:44 "

From 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.

What about

What about files outside the project directory?

If you are loading or sourcing files outside the project directory, the recommended way is to use an environment variable at the Operating System level. Other users of your R code on different laptops or servers would need to set the same environment variable. The advantage is that it is portable.

data_path <- Sys.getenv("PROJECT_DATA")
df <- read.csv(file.path(data_path, "file_name.csv"))

Note: There is a long list of environmental variables which can affect an R session.

What about many projects sourcing each other?

It's time to create an R package.

Paul Rougieux
  • 10,289
  • 4
  • 68
  • 110
  • 5
    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
  • @abalter I have added a section related to files outside the project folder. – Paul Rougieux Oct 19 '22 at 07:12
8

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()
print(this_file)
Alexis Lucattini
  • 1,211
  • 9
  • 13
7

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
  • 71
  • 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
  • 3
    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
  • Returns NULL for me in RStudio – Matthias Munz Sep 30 '22 at 09:44
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
  • 673
  • 6
  • 20
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,915
  • 1
  • 30
  • 48
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
  • 41,291
  • 27
  • 223
  • 311