300

I have a script called foo.R that includes another script other.R, which is in the same directory:

#!/usr/bin/env Rscript
message("Hello")
source("other.R")

But I want R to find that other.R no matter what the current working directory.

In other words, foo.R needs to know its own path. How can I do that?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Frank
  • 64,140
  • 93
  • 237
  • 324
  • 2
    No. :( I haven't seen any solution that actually works. Apart from the workaround to just pass the directory in or use an environment variable. – Frank Dec 04 '09 at 16:09
  • I hate having to rely on environment variables. – Frank Dec 04 '09 at 16:09
  • Try with `system("locate other.R")`... but be sure to give your script a unique name... O_o (assuming that you use UNIX system and that `locate` command is available) – aL3xa Jul 08 '10 at 12:12
  • 4
    This would be amazing to make scripts fully portable and executable by even R neofites! – Etienne Low-Décarie Apr 03 '12 at 18:15
  • 5
    It appears like all the answers require you to input the path at some point (at least to source the file)! It would be great if you could send someone a compressed folder and running any R script file within that folder would read from and save to that folder. – Etienne Low-Décarie Apr 03 '12 at 18:18
  • 17
    this single issue could actually become te reason why I could completely move to Python – Giacomo Oct 29 '17 at 12:47
  • 9
    @giac_man, I feel R is full of hundreds of tiny problems like this that all add up to making very difficult to work in. – Michael Barton Oct 31 '17 at 18:33
  • in Matlab there is the magic command 'mfilename' swich gives you the file name of currently running code. It cannot be so difficult to add something like that in R! – mjs Dec 04 '19 at 11:00
  • 2
    Great package from @Andrew , perfect for logging purposes. Many thanks for putting this on CRAN. – M_Merciless Dec 09 '20 at 10:42
  • 1
    this.path::this.path() – Aegis Aug 19 '22 at 18:44

31 Answers31

117

Here there is a simple solution for the problem. This command:

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

returns the path of the current script file. It works after the script was saved.

Frank
  • 64,140
  • 93
  • 237
  • 324
this.is.not.a.nick
  • 2,631
  • 3
  • 21
  • 26
  • 9
    It doesn't work for me. I run R in Windows. Any idea? – Ehsan88 Sep 25 '14 at 12:38
  • You always have to save the document befor running the script. Maybe is this the problem. – this.is.not.a.nick Sep 26 '14 at 20:44
  • 5
    Got the same error, with a saved scriptt and freshly installed and run R 3.2.0 on windows... – RalfB May 20 '15 at 09:57
  • 35
    This error happens when you try to execute `dirname(sys.frame(1)$ofile)` directly from Rstudio. It works ok when the script is executed using source("other.R"), and `dirname(sys.frame(1)$ofile)` is inside `"other.R"`. – Murta Jul 14 '15 at 23:39
  • it's working under Mac.. this should be executed from a script file, nit directly from R terminal – Ahmed Laatabi Oct 07 '15 at 14:25
  • This option works fine in Mac with R-Studio. Thank-you! – user9869932 Dec 23 '15 at 06:33
  • 13
    I got the 'not that many frames on the stack' error when calling as a script with rscript.exe i.e. not using source(). so I had to instead use the solution from Suppressingfire below – Mark Adamson Feb 25 '16 at 16:49
  • Did not work for me as well (Win10, fresh R.3.3.0, ran from cmd line). Answer from @Suppressingfire below worked fine. – davidski May 26 '16 at 16:58
  • This doesn't work when using debugSource as the relevant property is then fileName rather than oFile. Just needs some simple if statements to get around it as shown in other solutions posted here like http://stackoverflow.com/a/32016824/538403 – Mark Adamson Jul 07 '16 at 13:30
  • 8
    I gel `NULL` when this is placed in server.R when using shiny – Paul Jan 18 '17 at 20:24
  • what is "ofile"?? – theforestecologist Jul 12 '17 at 05:08
  • **SOLUTION:** `user@laptop_model:~$ R -e 'source("/home/user/[path_to_file].R")'` -- relative paths work like magic – Ufos Jan 25 '18 at 15:31
  • For me this works sometimes while rest of the time I get `Error in path.expand(path) : invalid 'path' argument` error. – quest Jan 31 '19 at 06:08
  • 1
    This solution didn't work for me with `Rscript dir/foo.R`. It did work with `R -e "source('dir/foo.R')"`. – Andrew Apr 25 '19 at 13:53
  • This does not work for me on Mac – htlbydgod Apr 01 '22 at 00:06
83

You can use the commandArgs function to get all the options that were passed by Rscript to the actual R interpreter and search them for --file=. If your script was launched from the path or if it was launched with a full path, the script.name below will start with a '/'. Otherwise, it must be relative to the cwd and you can concat the two paths to get the full path.

Edit: it sounds like you'd only need the script.name above and to strip off the final component of the path. I've removed the unneeded cwd() sample and cleaned up the main script and posted my other.R. Just save off this script and the other.R script into the same directory, chmod +x them, and run the main script.

main.R:

#!/usr/bin/env Rscript
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)
other.name <- file.path(script.basename, "other.R")
print(paste("Sourcing",other.name,"from",script.name))
source(other.name)

other.R:

print("hello")

output:

burner@firefighter:~$ main.R
[1] "Sourcing /home/burner/bin/other.R from /home/burner/bin/main.R"
[1] "hello"
burner@firefighter:~$ bin/main.R
[1] "Sourcing bin/other.R from bin/main.R"
[1] "hello"
burner@firefighter:~$ cd bin
burner@firefighter:~/bin$ main.R
[1] "Sourcing ./other.R from ./main.R"
[1] "hello"

This is what I believe dehmann is looking for.

MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
Suppressingfire
  • 3,246
  • 23
  • 17
  • 3
    I downmodded because your technique doesn't work with `source` as I thought the OP wanted - but maybe I misread his/her requirement. But I can't un-downmod :( Sorry! – hadley Nov 30 '09 at 16:26
  • 1
    But actually, it does work fine with source! Just source(other.name) and it works properly. – Suppressingfire Dec 01 '09 at 00:22
  • I think maybe we're talking at cross purposes. I think we have different understandings of what the dehmann is interested in doing. – Suppressingfire Dec 01 '09 at 00:23
  • 3
    For path concatenation, better to use `other.name <- file.path(script.basename, "other.R")` – Jason Sep 09 '14 at 14:34
  • 2
    When I try to run `commandArgs(trailingOnly = FALSE)` inside server.R in a shiny application, I get `[1] "RStudio" "--interactive"`. No information about the directory it was called from. – Paul Jan 18 '17 at 20:28
  • After all these years I consider this to be the best and cleanest answer. – Pranasas Jan 02 '18 at 15:09
  • In windows/Rstudio it returns `character(0)` – understorey Jun 01 '22 at 08:32
61

I couldn't get Suppressingfire's solution to work when 'source'ing from the R console.
I couldn't get hadley's solution to work when using Rscript.

Best of both worlds?

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))
        }
}
steamer25
  • 9,278
  • 1
  • 33
  • 38
  • 7
    I like this because it works with both `Rscript` and `source()` within R. I'd suggest doing `normalizePath()` on both versions, so that it gives the full path in both cases. – wch Nov 12 '14 at 19:57
  • 1
    This is the only thing that worked. Note, for this to work `library(base)` took me a while to figure that out lol – O.rka Oct 04 '16 at 18:32
  • 2
    you sir get my vote, because this is the solution that worked for me – Vince W. Mar 19 '18 at 04:30
  • 2
    If this helps anyone, for the original post, that would mean `source(file.path(dirname(thisFile()), "other.R"))` in `foo.R`. This works for me. – Kim May 12 '19 at 23:20
  • One issue. Suppose in RStudio I source `main.R` which sources `helper.R` which calls `thisFile()`. It will fetch the path of `main.R` instead of `helper.R`. Any tips here? – Wassadamo May 29 '19 at 20:07
  • This does not work for me in RStudio/Windows from `sys.frames()[[1]]$ofile` I get `NULL`. – understorey Jun 01 '22 at 08:36
38
frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])

Don't ask me how it works though, because I've forgotten :/

hadley
  • 102,019
  • 32
  • 183
  • 245
  • 3
    In what context does that work? print(sys.frames()) turns up NULL when I run it. – Suppressingfire Nov 30 '09 at 05:21
  • 1
    @Suppressingfire: `sys.frames` returns the environments of the call stack, so it only really makes sense when called from a function. Try, e.g., `foo <- function() {bar <- function() print(sys.frames()); bar()}; foo()`. I can't figure out @hadley's code though because environments don't have an `ofile` member. – Richie Cotton Nov 30 '09 at 11:27
  • 1
    You have to source the file in - i.e. if I save that code then run `source("~/code/test.r")`, `PATH` will be set to `~/desktop`. If you just evaluate it at the top level, it will return NULL. – hadley Nov 30 '09 at 16:24
  • 4
    This does not answer my question. I need to automatically find the "other.R" file. `x$ofile` is undefined, so `frame_files` is empty. – Frank Dec 04 '09 at 16:08
  • @hadley, very useful code. I was able to generalize the "reload current script" utility function I add to almost all scripts when they are in active development. [RScript reloader](https://gist.github.com/2946052) – Sim Jun 17 '12 at 23:29
  • I do not see `ofile` but do see `fileName` – PeterVermont Apr 14 '17 at 15:11
  • This worked for me in windows, in linux, but then it failed when I called it from crontab. SAD! – geneorama Jun 14 '17 at 21:11
  • As I just figured what I want on how sys.frames work from previous answer, I have found this solution. `sys.frames()` will get stack of environment from the most recent to the oldest one. This include `source`, which will set the `ofile` atribute, and non sourced environment, such as functions, which do not have `ofile` atribute. This will simply find the latest environment with the `ofile` atribute. Brilliant. – Colombo Mar 12 '20 at 03:27
34

This works for me

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
ColinTea
  • 998
  • 1
  • 9
  • 15
  • 7
    This only works from inside RStudio I guess. Trying from the terminal I get `Error: RStudio not running`. – Ista Oct 08 '18 at 11:26
  • 1
    more specifically it works, if run from a R script in R studio. Even on the console in RStudio it will not give the right result ```""``` in my case – Kay May 19 '20 at 12:14
  • This works while running interactively in Rstudio *as long as you don't change the document in focus*. If you submit lines to run and then switch to another document while they run, the path to the other document will be returned. – Patrick Jun 23 '20 at 14:49
29

The answer of rakensi from Getting path of an R script is the most correct and really brilliant IMHO. Yet, it's still a hack incorporating a dummy function. I'm quoting it here, in order to have it easier found by others.

sourceDir <- getSrcDirectory(function(dummy) {dummy})

This gives the directory of the file where the statement was placed (where the dummy function is defined). It can then be used to set the working direcory and use relative paths e.g.

setwd(sourceDir)
source("other.R")

or to create absolute paths

 source(paste(sourceDir, "/other.R", sep=""))
cuffel
  • 499
  • 5
  • 7
  • 2
    For me, your solution was the best. Specially because it could be applied to a Shiny app and that one on link not. – jcarlos Apr 18 '16 at 11:30
  • 1
    Here the getSrcDirectory is [utils::getSrcDirectory](https://stat.ethz.ch/R-manual/R-devel/library/utils/html/sourceutils.html) – RubenLaguna Oct 25 '16 at 07:53
  • 10
    This might work nicely under Linux/Mac, but it did not work for me in an interative RStudio session under Windows. `sourceDir` was blank. – Contango Aug 08 '17 at 06:48
  • 2
    @Contango on an interactive terminal, there is no path!!! You want the path to a file. – pommedeterresautee Jan 04 '18 at 10:18
  • 3
    I'm getting `character(0)`. Suggestions? – abalter Aug 01 '19 at 20:38
  • RStudio returns a blank value when I try this on a Linux server in an interactive session. – Ryan Howard Dec 09 '22 at 03:23
  • 1
    @RyanHoward This solution is for determining the location of the file where the dummy function was defined. Thus, it requires sourcing the R script file and will not work in console or using RStudio->Run because then no file is involved. Hope this helps. – cuffel Dec 10 '22 at 13:29
21

I've made a package for this, available on CRAN and GitHub, called this.path. The current version is 2.0.0 (2023-08-08), you can find it here:

https://CRAN.R-project.org/package=this.path

https://github.com/ArcadeAntics/this.path

Install it from CRAN:

utils::install.packages("this.path")

or install the development version from GitHub:

utils::install.packages("this.path",
    repos = "https://raw.githubusercontent.com/ArcadeAntics/PACKAGES")

and then use it by:

this.path::this.path()

or

library(this.path)
this.path()

The answer below is my original answer, kept just for reference, though it is quite a bit less functional than the most recent versions available above. Improvements include:

  • compatibility with more GUIs: 'Rgui' on Windows, 'VSCode', and 'Jupyter'
  • compatibility with packages testthat, knitr, compiler, and box, particularly testthat::source_file(), knitr::knit(), compiler::loadcmp(), and box::use()
  • handling filenames with spaces when running an R script from a shell under Unix-alikes
  • handling both uses of running an R script from a shell (-f FILE and --file=FILE)
  • correctly normalizes the path when using source() with argument (chdir = TRUE)
  • handling of file URIs with source() such as source("file:///path/to/file") and source("file:///C:/path/to/file")
  • improved handling of a connection instead of a character string within source()
  • handling of URL pathnames in source(), that is:
source("https://host/path/to/file")

if this.path() was used within the file, it would return "https://host/path/to/file". This also works for URLs beginning with "http://", "ftp://", and "ftps://". As an example, try:

source("https://raw.githubusercontent.com/ArcadeAntics/this.path/main/tests/this.path_w_URLs.R")
  • introduces functions here() / / this.proj(), similar to here::here(), for specifying absolute file paths relative to the executing script's directory / / executing script's project root
  • saving the normalized path within its appropriate environment the first time this.path() is called within a script, making it faster to use subsequent times within the same script and being independent of working directory. This means that setwd() will no longer break this.path() (as long as setwd() is used AFTER the first call to this.path() within that script)

Original Answer:

My answer is an improvement upon Jerry T's answer. The issue I found is that they are guessing whether a source() call was made by checking if variable ofile is found in the first frame on the stack. This will not work with nested source calls, nor source calls made from a non-global environment. Additionally, the order is wrong. We must look for source call BEFORE checking the shell arguments. Here is my solution:

this.path <- function (verbose = getOption("verbose"))
{
    ## loop through functions that lead here from most recent to
    ## earliest looking for an appropriate source call (a call to
    ## function source / / sys.source / / debugSource in RStudio)
    ##
    ## an appropriate source call is one in which the file argument has
    ## been evaluated (forced)
    ##
    ## for example, `source(this.path())` is an inappropriate source
    ## call. the argument 'file' is stored as a promise containing the
    ## expression "this.path()". when the value of 'file' is requested,
    ## the expression is evaluated at which time there should be two
    ## functions on the calling stack being 'source' and 'this.path'.
    ## clearly, you don't want to request the 'file' argument from that
    ## source call because the value of 'file' is under evaluation
    ## right now! the trick is to ask if 'file' has already been
    ## evaluated, the easiest way of which is to ask if a variable
    ## exists, one which is only created after the expression is
    ## necessarily evaluated.
    ##
    ## if that variable does exist, then argument 'file' has been
    ## forced and the source call is deemed appropriate. For 'source',
    ## the filename we want is the variable 'ofile' from that
    ## function's evaluation environment. For 'sys.source', the
    ## filename we want is the variable 'file' from that function's
    ## evaluation environment.
    ##
    ## if that variable does NOT exist, then argument 'file' hasn't
    ## been forced and the source call is deemed inappropriate. the
    ## 'for' loop moves to the next function up the calling stack
    ##
    ## unfortunately, there is no way to check the argument 'fileName'
    ## has been forced for 'debugSource' since all the work is done
    ## internally in C. Instead, we have to use a 'tryCatch' statement.
    ## When we ask for an object by name using 'get', R is capable of
    ## realizing if a variable is asking for its own definition (a
    ## recursive promise). The exact error is "promise already under
    ## evaluation" which indicates that the promise evaluation is
    ## requesting its own value. So we use the 'tryCatch' to get the
    ## argument 'fileName' from the evaluation environment of
    ## 'debugSource', and if it does not raise an error, then we are
    ## safe to return that value. If not, the condition returns false
    ## and the 'for' loop moves to the next function up the calling
    ## stack


    debugSource <- if (.Platform$GUI == "RStudio")
        get("debugSource", "tools:rstudio", inherits = FALSE)
    for (n in seq.int(to = 1L, by = -1L, length.out = sys.nframe() - 1L)) {
        if (identical(sys.function(n), source) &&
            exists("ofile", envir = sys.frame(n), inherits = FALSE))
        {
            path <- get("ofile", envir = sys.frame(n), inherits = FALSE)
            if (!is.character(path))
                path <- summary.connection(path)$description
            if (verbose)
                cat("Source: call to function source\n")
            return(normalizePath(path, "/", TRUE))
        }
        else if (identical(sys.function(n), sys.source) &&
                 exists("exprs", envir = sys.frame(n), inherits = FALSE))
        {
            path <- get("file", envir = sys.frame(n), inherits = FALSE)
            if (verbose)
                cat("Source: call to function sys.source\n")
            return(normalizePath(path, "/", TRUE))
        }
        else if (identical(sys.function(n), debugSource) &&
                 tryCatch({
                     path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
                     TRUE
                 }, error = function(c) FALSE))
        {
            if (verbose)
                cat("Source: call to function debugSource in RStudio\n")
            return(normalizePath(path, "/", TRUE))
        }
    }


    ## no appropriate source call was found up the calling stack


    ## running from RStudio
    if (.Platform$GUI == "RStudio") {


        ## ".rs.api.getSourceEditorContext" from "tools:rstudio"
        ## returns a list of information about the document open in the
        ## current tab
        ##
        ## element 'path' is a character string, the document's path


        context <- get(".rs.api.getSourceEditorContext",
            "tools:rstudio", inherits = FALSE)()
        if (is.null(context))
            stop("'this.path' used in an inappropriate fashion\n",
                 "* no appropriate source call was found up the calling stack\n",
                 "* R is being run from RStudio with no documents open\n",
                 "  (or source document has no path)")


        path <- context[["path"]]
        if (nzchar(path)) {
            Encoding(path) <- "UTF-8"
            if (verbose)
                cat("Source: document in RStudio\n")
            return(normalizePath(path, "/", TRUE))
        }
        else stop("'this.path' used in an inappropriate fashion\n",
                  "* no appropriate source call was found up the calling stack\n",
                  "* document in RStudio does not exist")
    }


    ## running from a shell
    else if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" ||  ## on Windows
             .Platform$OS.type == "unix"    && .Platform$GUI == "X11")      ## under Unix-alikes
    {


        argv <- commandArgs()
        ## remove all trailing arguments
        m <- match("--args", argv, 0L)
        if (m)
            argv <- argv[seq_len(m)]
        argv <- argv[-1L]


        ## get all arguments starting with "--file="
        FILE <- argv[startsWith(argv, "--file=")]
        ## remove "--file=" from the start of each string
        FILE <- substring(FILE, 8L)
        ## remove strings "-"
        FILE <- FILE[FILE != "-"]
        n <- length(FILE)
        if (n) {
            FILE <- FILE[[n]]
            if (verbose)
                cat("Source: shell argument 'FILE'\n")
            return(normalizePath(FILE, "/", TRUE))
        } else {
            stop("'this.path' used in an inappropriate fashion\n",
                 "* no appropriate source call was found up the calling stack\n",
                 "* R is being run from a shell where argument 'FILE' is missing")
        }
    }


    ## running from RGui on Windows
    else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {
        stop("'this.path' used in an inappropriate fashion\n",
             "* no appropriate source call was found up the calling stack\n",
             "* R is being run from Rgui which is currently unimplemented",
             "  consider using RStudio until such a time when this is implemented")
    }


    ## running from RGui on macOS
    else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {
        stop("'this.path' used in an inappropriate fashion\n",
             "* no appropriate source call was found up the calling stack\n",
             "* R is being run from AQUA which is currently unimplemented\n",
             "  consider using RStudio until such a time when this is implemented")
    }


    ## otherwise
    else stop("'this.path' used in an inappropriate fashion\n",
              "* no appropriate source call was found up the calling stack\n",
              "* R is being run in an unrecognized manner")
}
Iris
  • 377
  • 2
  • 7
  • 1
    When running this command on RGui, I get the following message. Any idea on how to get around it? Error in this.path::this.path() : 'this.path' used in an inappropriate fashion * no appropriate 'source' or 'sys.source' call was found up the calling stack * R is being run from RGui which requires a 'source' and 'sys.source' call on the calling stack – johnny Dec 02 '20 at 04:28
  • 1
    I hadn't realized until you commented that you could run code from within a script from 'RGui', I thought previously that the only way to run code within a script from 'RGui' was to use 'source'. I'm looking into a fix for this issue, hopefully I'll find something soon. For now, you could use 'RStudio' to edit and run your scripts because I know it works from there. Sorry for the lack of an answer, but thank you for pointing out this bug! – Iris Dec 02 '20 at 05:34
  • 1
    @johnny I believe I found a solution, but it only works on a Windows OS. I'm attempting to find a solution for the macOS version of "RGui" named "AQUA", and then I'll upload the update to the package to CRAN. It'll be about ~10 business days before the update is approved for release by one of the CRAN maintainers, hopefully 'RStudio' is working for you in the meantime! – Iris Dec 02 '20 at 16:13
  • 1
    @johnny the update was released a few hours ago, much sooner than I was expecting. I've tested on two separate computers now, it seems to work as intended from 'RGui'! – Iris Dec 03 '20 at 14:22
  • 2
    Just tested v.0.2.0 on a script file saved in an RGui session, and it works for me. Thanks! – johnny Dec 03 '20 at 15:02
  • 1
    I get the following error when I try to run it in VSCode: `Error in this.path() : 'this.path' used in an inappropriate fashion. * no appropriate source call was found up the calling stack. * R is being run from a shell where argument 'FILE' is missing`. – coip Jul 09 '22 at 03:20
  • @coip I hadn't implemented this.path() with VSCode because I mainly use RStudio and Rgui, but I believe I have it working now, you can find it from the GitHub installation above. This update won't be on CRAN for a while, I recently submitted an unrelated update, and the CRAN maintainers don't like updates so close together. Let me know if it works for you! – Iris Jul 15 '22 at 05:47
  • 1
    @Andrew Thanks for the response and for trying to update your package accordingly. I installed the GitHub version as you mentioned and tried it in VSCode again. Unfortunately, it still gives an error message. The error message is exactly the same as the one I wrote above except that `Error in this.path() :` has now changed to `Error in this.path(verbose) :`. – coip Jul 16 '22 at 17:48
  • @coip Would you mind if we moved this to https://github.com/ArcadeAntics/this.path/issues I think it would be easier to get an idea of what is wrong with a larger character limit and the ability to share screenshots, thank you! – Iris Jul 17 '22 at 03:04
  • 1
    This should be the right answer, it's the only one that's working, at least for me. – GitHunter0 Sep 17 '22 at 16:08
  • 1
    currently working in R studio console/in-line & sourced – pep Oct 01 '22 at 14:49
19

My all in one! (--01/09/2019 updated to deal with RStudio Console)

#' current script file (in full path)
#' @description current script file (in full path)
#' @examples
#' works with Rscript, source() or in RStudio Run selection, RStudio Console
#' @export
ez.csf <- function() {
    # http://stackoverflow.com/a/32016824/2292993
    cmdArgs = commandArgs(trailingOnly = FALSE)
    needle = "--file="
    match = grep(needle, cmdArgs)
    if (length(match) > 0) {
        # Rscript via command line
        return(normalizePath(sub(needle, "", cmdArgs[match])))
    } else {
        ls_vars = ls(sys.frames()[[1]])
        if ("fileName" %in% ls_vars) {
            # Source'd via RStudio
            return(normalizePath(sys.frames()[[1]]$fileName))
        } else {
            if (!is.null(sys.frames()[[1]]$ofile)) {
            # Source'd via R console
            return(normalizePath(sys.frames()[[1]]$ofile))
            } else {
                # RStudio Run Selection
                # http://stackoverflow.com/a/35842176/2292993
                pth = rstudioapi::getActiveDocumentContext()$path
                if (pth!='') {
                    return(normalizePath(pth))
                } else {
                    # RStudio Console
                    tryCatch({
                            pth = rstudioapi::getSourceEditorContext()$path
                            pth = normalizePath(pth)
                        }, error = function(e) {
                            # normalizePath('') issues warning/error
                            pth = ''
                        }
                    )
                    return(pth)
                }
            }
        }
    }
}
Jerry T
  • 1,541
  • 1
  • 19
  • 17
  • Doesn't work with interactive R session; I'm getting: ``` > source("csf.R") > csf() Error: RStudio not running ``` – ManicMailman Jul 06 '17 at 23:57
  • This is great. Can someone make a package? – Joe Flack Jun 13 '20 at 02:44
  • This works while running interactively in Rstudio as long as you don't change the document in focus. If you submit lines to run and then switch to another document while they run, the path to the other document will be returned. – Patrick Jun 23 '20 at 14:53
13

A slimmed down variant of Supressingfire's answer:

source_local <- function(fname){
    argv <- commandArgs(trailingOnly = FALSE)
    base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
    source(paste(base_dir, fname, sep="/"))
}
momeara
  • 1,341
  • 2
  • 17
  • 29
11

This works for me. Just greps it out of the command line arguments, strips off the unwanted text, does a dirname and finally gets the full path from that:

args <- commandArgs(trailingOnly = F)  
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
eddi
  • 49,088
  • 6
  • 104
  • 155
10

I have wrapped up and extended the answers to this question into a new function thisfile() in rprojroot. Also works for knitting with knitr.

Kayle Sawyer
  • 549
  • 7
  • 22
krlmlr
  • 25,056
  • 14
  • 120
  • 217
8

I tried almost everything from this question, Getting path of an R script, Get the path of current script, Find location of current .R file and R command for setting working directory to source file location in Rstudio, but at the end found myself manually browsing the CRAN table and found

scriptName library

which provides current_filename() function, which returns proper full path of the script when sourcing in RStudio and also when invoking via R or RScript executable.

Bojan P.
  • 942
  • 1
  • 10
  • 21
6

I liked steamer25's solution as it seems the most robust for my purposes. However, when debugging in RStudio (in windows), the path would not get set properly. The reason being that if a breakpoint is set in RStudio, sourcing the file uses an alternate "debug source" command which sets the script path a little differently. Here is the final version which I am currently using which accounts for this alternate behavior within RStudio when debugging:

# @return full path to this script
get_script_path <- function() {
    cmdArgs = commandArgs(trailingOnly = FALSE)
    needle = "--file="
    match = grep(needle, cmdArgs)
    if (length(match) > 0) {
        # Rscript
        return(normalizePath(sub(needle, "", cmdArgs[match])))
    } else {
        ls_vars = ls(sys.frames()[[1]])
        if ("fileName" %in% ls_vars) {
            # Source'd via RStudio
            return(normalizePath(sys.frames()[[1]]$fileName)) 
        } else {
            # Source'd via R console
            return(normalizePath(sys.frames()[[1]]$ofile))
        }
    }
}
aprstar
  • 81
  • 1
  • 5
  • 1
    source in Rstudio gave ofile for me, but debugSource gave fileName so your solution works well but the code comments aren't quite right in my case – Mark Adamson Jul 07 '16 at 13:32
4

I also had this problem, and none of the above solutions worked for me. Maybe with the source or things like that, but it was not clear enough.

I found this, for me elegant, solution:

paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")

The important thing in that is the fileSnapshot() that gives you a lot of information about a file. It returns a list of 8 elements. When you pick path as the list element, the path is returned with \\ as separator, so the rest of the code is just to change that.

I hope this helps.

Antoine
  • 269
  • 1
  • 10
  • 2
    This did not work for me on a Linux machine; instead of returning the path of the file, it returned the directory I was currently located in. I created a test script called TEST.R with one line of code: print(fileSnapshot()$path) I saved it in this folder: /opt/home/boops/Desktop/Testfolder/TEST.R I then navigated to my desktop and tried to run the file: boops@linuxserver:~/Desktop$ Rscript /opt/home/boops/Desktop/Testfolder/TEST.R [1] "/opt/home/boops/Desktop" – Boops Boops Jan 07 '20 at 18:33
  • 1
    Didn't work for me either. Returns the same thing as 'here()' when using the 'here' library. It returned the path to my currently open R project, but not he very file itself being executed. – Joe Flack Jun 13 '20 at 02:39
3

I just worked this out myself. To ensure portability of your script always begin it with:

wd <- setwd(".")
setwd(wd)

It works because "." translates like the Unix command $PWD. Assigning this string to a character object allows you to then insert that character object into setwd() and Presto your code will always run with its current directory as the working directory, no matter whose machine it is on or where in the file structure it is located. (Extra bonus: The wd object can be used with file.path() (ie. file.path(wd, "output_directory") to allow for the creation of a standard output directory regardless of the file path leading to your named directory. This does require you to make the new directory before referencing it this way but that, too, can be aided with the wd object.

Alternately, the following code performs the exact same thing:

wd <- getwd()
setwd(wd)

or, if you don't need the file path in an object you can simply:

setwd(".")
2

You can wrap the r script in a bash script and retrieve the script's path as a bash variable like so:

#!/bin/bash
     # [environment variables can be set here]
     path_to_script=$(dirname $0)

     R --slave<<EOF
        source("$path_to_script/other.R")

     EOF
ennuikiller
  • 46,381
  • 14
  • 112
  • 137
  • 4
    This requires you to have the script path. It does not allow you to make a truly portable R script that can run from anywhere. – Etienne Low-Décarie Apr 03 '12 at 18:19
  • @EtienneLow-Décarie It does not require the script path, it gets it from bash. The main issue is that it is not a reliable way to get the path. Something like this is preferred, as in https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within path_to_script="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" – John Haberstroh Aug 28 '17 at 21:00
2

I like this approach:

this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
beroe
  • 11,784
  • 5
  • 34
  • 79
2

Note that the getopt package provides the get_Rscript_filename function, which just uses the same solution presented here, but is already written for you in a standard R module, so you don't have to copy and paste the "get script path" function into every script you write.

Ryan C. Thompson
  • 40,856
  • 28
  • 97
  • 159
2

Steamer25's approach works, but only if there is no whitespace in the path. On macOS at least the cmdArgs[match] returns something like /base/some~+~dir~+~with~+~whitespace/ for /base/some\ dir\ with\ whitespace/.

I worked around this by replacing the "~+~" with a simple whitespace before returning it.

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

Obviously you can still extend the else block like aprstar did.

iball
  • 31
  • 3
2

If rather than the script, foo.R, knowing its path location, if you can change your code to always reference all source'd paths from a common root then these may be a great help:

Given

  • /app/deeply/nested/foo.R
  • /app/other.R

This will work

#!/usr/bin/env Rscript
library(here)
source(here("other.R"))

See https://rprojroot.r-lib.org/ for how to define project roots.

discipulus
  • 2,665
  • 3
  • 34
  • 51
mmell
  • 2,448
  • 1
  • 24
  • 16
1

I had issues with the implementations above as my script is operated from a symlinked directory, or at least that's why I think the above solutions didn't work for me. Along the lines of @ennuikiller's answer, I wrapped my Rscript in bash. I set the path variable using pwd -P, which resolves symlinked directory structures. Then pass the path into the Rscript.

Bash.sh

#!/bin/bash

# set path variable
path=`pwd -P`

#Run Rscript with path argument
Rscript foo.R $path

foo.R

args <- commandArgs(trailingOnly=TRUE)
setwd(args[1])
source(other.R)
Luke Singham
  • 1,536
  • 2
  • 20
  • 38
1

I would use a variant of @steamer25 's approach. The point is that I prefer to obtain the last sourced script even when my session was started through Rscript. The following snippet, when included on a file, will provided a variable thisScript containing the normalized path of the script. I confess the (ab)use of source'ing, so sometimes I invoke Rscript and the script provided in the --file argument sources another script that sources another one... Someday I will invest in making my messy code turns into a package.

thisScript <- (function() {
  lastScriptSourced <- tail(unlist(lapply(sys.frames(), function(env) env$ofile)), 1)

  if (is.null(lastScriptSourced)) {
    # No script sourced, checking invocation through Rscript
    cmdArgs <- commandArgs(trailingOnly = FALSE)
    needle <- "--file="
    match <- grep(needle, cmdArgs)
    if (length(match) > 0) {
      return(normalizePath(sub(needle, "", cmdArgs[match]), winslash=.Platform$file.sep, mustWork=TRUE))
    }
  } else {
    # 'source'd via R console
    return(normalizePath(lastScriptSourced, winslash=.Platform$file.sep, mustWork=TRUE))
  }
})()
1

99% of the cases you might simply use:

sys.calls()[[1]] [[2]]

It will not work for crazy calls where the script is not the first argument, i.e., source(some args, file="myscript"). Use @hadley's in these fancy cases.

antonio
  • 10,629
  • 13
  • 68
  • 136
1

By looking at the call stack we can get the filepath of each script being executed, the two most useful will probably either be the currently executing script, or the first script to be sourced (entry).

script.dir.executing = (function() return( if(length(sys.parents())==1) getwd() else dirname( Filter(is.character,lapply(rev(sys.frames()),function(x) x$ofile))[[1]] ) ))()

script.dir.entry = (function() return( if(length(sys.parents())==1) getwd() else dirname(sys.frame(1)$ofile) ))()
user425678
  • 720
  • 7
  • 17
1

The most flexible solution to this that I have found utilizes rstudioapi::getSourceEditorContext() and (optionally) sub()

  • Work interactively for both .Rmd and .R scripts
  • Works when knitting a .Rmd file
  • Works when sourcing a .R file

Try the following:

current_file <-
  rstudioapi::getSourceEditorContext()$path %>%
  sub(".*/", "", .)

The rstudioapi::getSourceEditorContext()$path returns the full path of the current file

The sub(".*/", "", .) extracts everything after the last /, leaving only the name of the file.

I hope that helps!

Lewkrr
  • 414
  • 4
  • 13
  • This “solution” was already posted, and the previous answer has several comments explaining why it is not a good solution. – Konrad Rudolph Jul 23 '23 at 14:43
  • Would you please include a link to the previously posted solution? I'd like to see the comments that explain why this approach is not a good one (for my own edification). – Lewkrr Aug 21 '23 at 16:18
  • See https://stackoverflow.com/a/36777602/1968 and https://stackoverflow.com/a/45844073/1968. – Konrad Rudolph Aug 22 '23 at 07:07
  • so far this is the only solution that works for me. @KonradRudolph why it is not a good solution? – Giacomo Aug 29 '23 at 16:51
  • @Giacomo Because it does not work. See the links in my previous comment. – Konrad Rudolph Aug 30 '23 at 07:04
  • ok I see what you mean, it is not the right answer to this question. However it does work for a different use case (running R scripts from R Studio) – Giacomo Aug 30 '23 at 08:07
0

See findSourceTraceback() of the R.utils package, which

Finds all 'srcfile' objects generated by source() in all call frames. This makes it possible to find out which files are currently scripted by source().

HenrikB
  • 6,132
  • 31
  • 34
0
#!/usr/bin/env Rscript
print("Hello")

# sad workaround but works :(
programDir <- dirname(sys.frame(1)$ofile)
source(paste(programDir,"other.R",sep='/'))
source(paste(programDir,"other-than-other.R",sep='/'))
kinjelom
  • 6,105
  • 3
  • 35
  • 61
  • 1
    I still get the error "Error in sys.frame(1) : not that many frames on the stack " – Michael Barton Oct 31 '17 at 18:47
  • This only works when using `source` or `sys.source`, and it always grabs the first `source` on the stack, not the most recent. – Iris Dec 11 '20 at 16:32
0

Amazing there is no '$0' type structure in R! You can do it with a system() call to a bash script written in R:

write.table(c("readlink -e $0"), file="scriptpath.sh",col=F, row=F, quote=F)
thisscript <- system("sh scriptpath.sh", intern = TRUE)

Then just split out the scriptpath.sh name for other.R

splitstr <- rev(strsplit(thisscript, "\\/")[[1]])
otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")
bruce.moran
  • 347
  • 2
  • 10
  • I get an error message `readLink: illegal option -- e usage: readLink [-FlLnqrsx] [-f format] [-t timefmt] [file ...]` – altabq May 07 '20 at 08:48
0

I work in an HPC cluster environment. I develop my code in a different location from where I do my production runs. During development, I'm usually calling R interactively from the command line (not using RStudio). There is lots of source("foo.R") going on.

During production runs, I usually write a bash script that tries different parameters and runs each set of parameters in a separate directory. The bash script utilizes the workload manager (i.e. SLURM). In this environment, it is trivial to set an environmental variable. With this in mind, the below solution works best for me.

other.R

my_message <- function(){
return("R is awkward")
}

foo.R

srcpath = Sys.getenv("R_SRC")
# Check if runnning w/o setting R_SRC - presumably done in directory of development, i.e. /path/to/R/code
if(srcpath == ""){
    srcpath="./"
}
source(sprintf("%s/other.R", srcpath))
string = my_message()
print(string)

If running this from the R interactive shell and within /path/to/R/code, simply do

> source("foo.R")

If running not from the interactive shell and not running from /path/to/R/code, set the environmental variable R_SRC first, then call Rscript

$ export R_SRC=/path/to/R/code/
$ Rscript /path/to/R/code/foo.R
irritable_phd_syndrome
  • 4,631
  • 3
  • 32
  • 60
0

The solution arrived in 2016. Many thanks to the author, Sahil Seth!

The package funr on CRAN and github provides the function sys.script() which gets the full path to the current script. It even references a similar SO post.

Thus, the solution is:

myscript.R:

#!/usr/bin/env Rscript
f  <-  funr::sys.script()
show(f)

and then executing the command:

user@somewhere:/home$ Rscript myscript.R

at the command line will output, e.g.:

"/home/path/to/myscript.R"

to the console.

cmo
  • 3,762
  • 4
  • 36
  • 64
  • This only handles two cases (script run via `source()` or from the command line) and even then does not always work (R can be invoked with `-f` instead of `--file=`). You should not rely on it. – Konrad Rudolph May 30 '23 at 20:11
0

Just to build on the above answers, as a safety check, you could add a wrapper that asks the user to find the file if (for whatever reason) sys.frame(1) fails (as it might if interactive() == TRUE), or the sourced script is not where the main script expects it to be.

fun_path = tryCatch(expr = 
                      {file.path(dirname(sys.frame(1)$ofile), "foo.R")},
                    error = function(e){'foo.R'}
                    )
if(!file.exists(fun_path))
{
  msg = 'Please select "foo.R"'
  # ask user to find data
  if(Sys.info()[['sysname']] == 'Windows'){#choose.files is only available on Windows
    message('\n\n',msg,'\n\n')
    Sys.sleep(0.5)#goes too fast for the user to see the message on some computers
    fun_path  = choose.files(
      default = file.path(gsub('\\\\', '/', Sys.getenv('USERPROFILE')),#user
                          'Documents'),
      caption = msg
    )
  }else{
    message('\n\n',msg,'\n\n')
    Sys.sleep(0.5)#goes too fast for the user to see the message on some computers
    fun_path = file.choose(new=F)
  }
}
#source the function
source(file = fun_path, 
       encoding = 'UTF-8')
Foztarz
  • 71
  • 6