74

Is there a way to programmatically find the path of an R script inside the script itself?

I am asking this because I have several scripts that use RGtk2 and load a GUI from a .glade file.

In these scripts I am obliged to put a setwd("path/to/the/script") instruction at the beginning, otherwise the .glade file (which is in the same directory) will not be found.

This is fine, but if I move the script in a different directory or to another computer I have to change the path. I know, it's not a big deal, but it would be nice to have something like:

setwd(getScriptPath())

So, does a similar function exist?

nico
  • 50,859
  • 17
  • 87
  • 112
  • 1
    This would be particularly useful in interactive sessions, such as when using Rstudio. – Quantum7 Feb 20 '15 at 16:10
  • 1
    It seems that many of these "path to file" problems may now be solved with the package rprojroot: https://cran.r-project.org/web/packages/rprojroot/index.html . rprojroot bills itself as "rprojroot: Finding Files in Project Subdirectories." A 'lightweight' version for interactive use is the here package: https://github.com/krlmlr/here . – BarkleyBG Oct 12 '17 at 19:11

13 Answers13

42

This works for me:

getSrcDirectory(function(x) {x})

This defines an anonymous function (that does nothing) inside the script, and then determines the source directory of that function, which is the directory where the script is.

rakensi
  • 1,437
  • 1
  • 15
  • 20
  • 38
    This does not work for me on Linux R 3.2.4. The output is blank. – biocyberman Apr 26 '16 at 09:38
  • 3
    Works for me in 3.3.2. @biocyberman: are you calling this from the console? Because it does return blank for me there. If from a script, are you printing it? (similarly, it works when Sourceing from Rstudio, bot not when Running the code). – naught101 Feb 01 '17 at 23:23
  • 1
    Works in 3.3.2 on OSX 10.11.6 BUT only when Sourcing the script in RStudio, as @naught101 wrote. Returns blank when called in console or when CMD-Return the line in script window. – mattek Feb 12 '17 at 20:44
  • 2
    Warning: this does not work if the working directory changed after sourcing the file AND the old working directory contained the file. The reason is that in this case the returned path is "." (i.e. the working directory), which is not true anymore if the working directory changes. – cno Aug 18 '17 at 14:18
  • 1
    I tried a lot of methods to get this simple thing right (I am on Windows 10, R 3.4.1, using Rstudio). Starting from sys.frame(1)$ofile, parent.frame(2)$ofile, etc. I think this answer works the best so far. – addicted Sep 14 '17 at 03:04
38

For RStudio only:

setwd(dirname(rstudioapi::getActiveDocumentContext()$path))

This works when Running or Sourceing your file.

Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
25

Use source("yourfile.R", chdir = T)

Boern
  • 7,233
  • 5
  • 55
  • 86
hadley
  • 102,019
  • 32
  • 183
  • 245
  • 2
    @nico I am new to R and I wanted to do a similar thing. I tried this solution but I am getting an error `Error: '...' used in an incorrect context`, can you please comment what the problem could be? – Dronacharya Jul 29 '13 at 14:07
  • 1
    @Dronacharya: easy! The `...` are just a placeholder for the filename! Put the name of the file you want to include and you're set – nico Jul 29 '13 at 14:27
  • 32
    This isn't widely applicable, especially not to the title of the question - you can't use it from within the script itself – if you know the path (to source the file), you can obviously set the path too. – Ruben Jul 30 '13 at 16:52
  • @Ruben: it worked for the specific problem at hand at the time (if I remember correctly, it has been a while...) but I can see how it may not be always a good solution. – nico Jul 30 '13 at 18:27
  • 1
    @Ruben it is usually better to assume that all paths are relative to the script you are sourcing. Any other solutions will be rather hacky. – hadley Jul 31 '13 at 13:17
  • 4
    @hadley This is what I do. Unfortunately that assumption will not always work (it will, when using `source`), [esp. in interactive sessions](http://stackoverflow.com/questions/8835426/get-filename-and-path-of-sourced-file/8852960?noredirect=1#comment26247034_8852960), incorrect working directories make it a major nuisance for me to share .r, .rmd etc. with non-technical colleagues, who will open an .r script that I sent and have a previous WD set when they execute all lines, so that relative paths fail. So, something like `__DIR__` in PHP would be useful, but yeah, hacky, if at all possible. – Ruben Jul 31 '13 at 16:59
  • @Ruben you could also try shipping RStudio projects which will take of that for you (and I strongly believe it's the job of your R environment, not R to take care of this) – hadley Jul 31 '13 at 19:32
  • 4
    @hadley Actually they don't. Projects set the WD to the project directory, which is not the same as source files in sub-folders of the project. So the relative paths I'd use for an Rstudio project would differ from those knitr uses. Most importantly though, you cannot programmatically reset the wd to whatever is proper, if it's been changed. I [filed a suggestion](http://support.rstudio.org/help/discussions/suggestions/4872-make-set-working-directory-to-source-file-location-useable-as-an-r-function) for this. I have no strong beliefs about where this functionality belongs, but it's a nuisance. – Ruben Jul 31 '13 at 19:47
  • 3
    I don't think this is a very good solution because the name of the current file needs to be given. What if the file is renamed? It wouldn't work any longer. Also, it didn't work when the program was run within RStudio. – Mr. Lance E Sloan Aug 03 '16 at 20:43
  • Another problem is that it lacks robustness. For instance, if I am in `.../somedir` and I call `.../script.R`, suppose I want `script.R` to source one file from `.../somedir` and another from `... – abalter Aug 01 '19 at 20:42
  • 2
    Another problem is if the script is run with `Rscript` rather than being `source`'d. – abalter Aug 01 '19 at 20:51
8

Exploit the implicit "--file" argument of Rscript

When calling the script using "Rscript" (Rscript doc) the full path of the script is given as a system parameter. The following function exploits this to extract the script directory:

getScriptPath <- function(){
    cmd.args <- commandArgs()
    m <- regexpr("(?<=^--file=).+", cmd.args, perl=TRUE)
    script.dir <- dirname(regmatches(cmd.args, m))
    if(length(script.dir) == 0) stop("can't determine script dir: please call the script with Rscript")
    if(length(script.dir) > 1) stop("can't determine script dir: more than one '--file' argument detected")
    return(script.dir)
}
Bernhard Kausler
  • 5,119
  • 3
  • 32
  • 36
  • 1
    When I run this (on Ubuntu 15.10 with R 3.2.3), the argument `--file` does not contain the full path, so the returned script dir is empty: ``` daniel@computer:~/data$ Rscript Script.R /usr/lib/R/bin/exec/R --slave --no-restore --file=Script.R ``` – Daniel N. Feb 23 '16 at 10:06
  • Works for me (win7, R3.4). – mdag02 Jul 18 '17 at 07:15
7

If you wrap your code in a package, you can always query parts of the package directory.
Here is an example from the RGtk2 package:

> system.file("ui", "demo.ui", package="RGtk2")
[1] "C:/opt/R/library/RGtk2/ui/demo.ui"
> 

You can do the same with a directory inst/glade/ in your sources which will become a directory glade/ in the installed package -- and system.file() will compute the path for you when installed, irrespective of the OS.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • Thanks Dirk. I really will have to start and think about putting my scripts in a package... I assume from your answer there is not a way to do it outside of a package? – nico Aug 10 '10 at 18:45
  • 1
    You can fudge something else -- set an environment variable and query that, or deparse the argument that supplied the script name to R, or ... but why not use something simple, tested, reliable and made for the purpose? ;-) – Dirk Eddelbuettel Aug 10 '10 at 19:04
  • Oh, of course, was just wondering if there was any other solution! Thanks! – nico Aug 10 '10 at 19:17
4

This answer works fine to me:

script.dir <- dirname(sys.frame(1)$ofile)

Note: script must be sourced in order to return correct path

I found it in: https://support.rstudio.com/hc/communities/public/questions/200895567-can-user-obtain-the-path-of-current-Project-s-directory-

But I still don´t understand what is sys.frame(1)$ofile. I didn´t find anything about that in R Documentation. Someone can explain it?

sietemonos
  • 101
  • 1
  • 2
  • yes. without source, one gets the following error: Error in dirname(parent.frame(2)$ofile) : a character vector argument expected – patapouf_ai Jun 11 '15 at 11:45
  • I know this answer is incredibly old, but here's why it works (sometimes). sys.frame(1) will return the first environment on the stack. The stack is a list that allows R to keep track of which functions have lead where. So if, in the R Console, you ran the command 'source( .. )', it would put three items on the stack, it would put source ( .. ) as the first item on the call stack, it would put 'source' as the first on the function stack, and it would put the environment to evaluate 'source( .. )' as the first on the frame stack. – Iris Dec 14 '20 at 15:30
  • The '$ofile' grabs the argument 'ofile' from the environment returned by 'sys.frame' which should be the filename used in 'source'. Be aware, this only grabs the filename of the first 'source' call made, so if you have multiple source calls, this may not work. – Iris Dec 14 '20 at 15:32
2
#' current script dir
#' @param
#' @return
#' @examples
#' works with source() or in RStudio Run selection
#' @export
z.csd <- function() {
    # http://stackoverflow.com/questions/1815606/rscript-determine-path-of-the-executing-script
    # must work with source()
    if (!is.null(res <- .thisfile_source())) res
    else if (!is.null(res <- .thisfile_rscript())) dirname(res)
    # http://stackoverflow.com/a/35842176/2292993  
    # RStudio only, can work without source()
    else dirname(rstudioapi::getActiveDocumentContext()$path)
}
# Helper functions
.thisfile_source <- function() {
    for (i in -(1:sys.nframe())) {
        if (identical(sys.function(i), base::source))
            return (normalizePath(sys.frame(i)$ofile))
    }

    NULL
}
.thisfile_rscript <- function() {
    cmdArgs <- commandArgs(trailingOnly = FALSE)
    cmdArgsTrailing <- commandArgs(trailingOnly = TRUE)
    cmdArgs <- cmdArgs[seq.int(from=1, length.out=length(cmdArgs) - length(cmdArgsTrailing))]
    res <- gsub("^(?:--file=(.*)|.*)$", "\\1", cmdArgs)

    # If multiple --file arguments are given, R uses the last one
    res <- tail(res[res != ""], 1)
    if (length(res) > 0)
        return (res)

    NULL
}
Jerry T
  • 1,541
  • 1
  • 19
  • 17
1

A lot of these solutions are several years old. While some may still work, there are good reasons against utilizing each of them (see linked source below). I have the best solution (also from source): use the here library.

Original example code:

library(ggplot2)
setwd("/Users/jenny/cuddly_broccoli/verbose_funicular/foofy/data")
df <- read.delim("raw_foofy_data.csv")

Revised code

library(ggplot2)
library(here)

df <- read.delim(here("data", "raw_foofy_data.csv"))

This solution is the most dynamic and robust because it works regardless of whether you are using the command line, RStudio, calling from an R script, etc. It is also extremely simple to use and is succinct.

Source: https://www.tidyverse.org/articles/2017/12/workflow-vs-script/

user2205916
  • 3,196
  • 11
  • 54
  • 82
  • 1
    This doesn't answer the question. – WDS May 13 '20 at 07:05
  • Unfortunately this does *not* really work when “calling from an R script”. It only works properly when following the project structure laid out in its documentation. It’s fine for analysis workflows but not for some other applications. – Konrad Rudolph Apr 24 '22 at 21:44
1

I have found something that works for me. setwd(dirname(rstudioapi::getActiveDocumentContext()$path))

delaye
  • 1,357
  • 27
  • 45
0

How about using system and shell commands? With the windows one, I think when you open the script in RStudio it sets the current shell directory to the directory of the script. You might have to add cd C:\ e.g or whatever drive you want to search (e.g. shell('dir C:\\*file_name /s', intern = TRUE) - \\ to escape escape character). Will only work for uniquely named files unless you further specify subdirectories (for Linux I started searching from /). In any case, if you know how to find something in the shell, this provides a layout to find it within R and return the directory. Should work whether you are sourcing or running the script but I haven't fully explored the potential bugs.

#Get operating system
OS<-Sys.info()
win<-length(grep("Windows",OS))
lin<-length(grep("Linux",OS))

#Find path of data directory
#Linux Bash Commands
if(lin==1){
  file_path<-system("find / -name 'file_name'", intern = TRUE)
  data_directory<-gsub('/file_name',"",file_path)
}
#Windows Command Prompt Commands
if(win==1){
  file_path<-shell('dir file_name /s', intern = TRUE)
  file_path<-file_path[4]
  file_path<-gsub(" Directory of ","",file_path)
  filepath<-gsub("\\\\","/",file_path)
  data_directory<-file_path
}

#Change working directory to location of data and sources  
setwd(data_directory)
WJ2016
  • 1
  • 1
0

Thank you for the function, though I had to adjust it a Little as following for me (W10):

#Windows Command Prompt Commands
if(win==1){
  file_path<-shell('dir file_name', intern = TRUE)
  file_path<-file_path[4]
  file_path<-gsub(" Verzeichnis von ","",file_path)
  file_path<-chartr("\\","/",file_path)
  data_directory<-file_path
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
0

In my case, I needed a way to copy the executing file to back up the original script together with its outputs. This is relatively important in research. What worked for me while running my script on the command line, was a mixure of other solutions presented here, that looks like this:

library(scriptName)
file_dir <- gsub("\\", "/", fileSnapshot()$path, fixed=TRUE)
file.copy(from = file.path(file_dir, scriptName::current_filename()) ,
          to = file.path(new_dir, scriptName::current_filename()))

Alternatively, one can add to the file name the date and our to help in distinguishing that file from the source like this:

file.copy(from = file.path(current_dir, current_filename()) ,
          to = file.path(new_dir, subDir, paste0(current_filename(),"_", Sys.time(), ".R")))
Marcos
  • 103
  • 6
  • 1
    after reading your reading 3 times i still don't get how this solves the problem. the OP is not asking for how to copy files. can you trim your answer down to the relevant parts and also how this is much better than the other solutions? Thanks – StupidWolf Jul 07 '20 at 22:19
  • @StupidWolf The function `scriptName::current_filename()` was the only one that was able to retrieve the file name on a Linux server with no Rstudio and without sourcing the file to be copied, as most of the solutions here with `sys.frame(1)$ofile` require you to do. When you get the file name you want, you just needed to find the current script directory, and that is handled by `fileSnapshot()$path` with some additional adjustments made with `gsub`. I hope it helps. – Marcos Jul 08 '20 at 06:24
0

None of the solutions given so far work in all circumstances. Worse, many solutions use setwd, and thus break code that expects the working directory to be, well, the working directory — i.e. the code that the user of the code chose (I realise that the question asks about setwd() but this doesn’t change the fact that this is generally a bad idea).

R simply has no built-in way to determine the path of the currently running piece of code.

A clean solution requires a systematic way of managing non-package code. That’s what ‘box’ does. With ‘box’, the directory relative to the currently executing code can be found trivially:

box::file()

However, that isn’t the purpose of ‘box’; it’s just a side-effect of what it actually does: it implements a proper, modern module system for R. This includes organising code in (nested) modules, and hence the ability to load code from modules relative to the currently running code.

To load code with ‘box’ you wouldn’t use e.g. source(file.path(box::file(), 'foo.r')). Instead, you’d use

box::use(./foo)

However, box::file() is still useful for locating data (i.e. OP’s use-case). So, for instance, to locate a file mygui.glade from the current module’s path, you would write.

glade_path = box::file('mygui.glade')

And (as long as you’re using ‘box’ modules) this always works, doesn’t require any hacks, and doesn’t use setwd.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214