Determine path of the executing script
Asked Answered
H

31

302

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?

Humankind answered 29/11, 2009 at 13:58 Comment(10)
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.Humankind
I hate having to rely on environment variables.Humankind
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)Clanton
This would be amazing to make scripts fully portable and executable by even R neofites!Justitia
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.Justitia
this single issue could actually become te reason why I could completely move to PythonLucilius
@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.Parrot
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!Seductive
Great package from @Andrew , perfect for logging purposes. Many thanks for putting this on CRAN.Footing
this.path::this.path()Koziel
I
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.

Illbred answered 16/4, 2013 at 20:4 Comment(15)
It doesn't work for me. I run R in Windows. Any idea?Twentyfourmo
You always have to save the document befor running the script. Maybe is this the problem.Illbred
Got the same error, with a saved scriptt and freshly installed and run R 3.2.0 on windows...Tangent
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".Scrutiny
it's working under Mac.. this should be executed from a script file, nit directly from R terminalNorton
This option works fine in Mac with R-Studio. Thank-you!Grettagreuze
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 belowAirel
Did not work for me as well (Win10, fresh R.3.3.0, ran from cmd line). Answer from @Suppressingfire below worked fine.Iconolatry
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 https://mcmap.net/q/99675/-determine-path-of-the-executing-scriptAirel
I gel NULL when this is placed in server.R when using shinyRefluent
what is "ofile"??Krongold
SOLUTION: user@laptop_model:~$ R -e 'source("/home/user/[path_to_file].R")' -- relative paths work like magicPatty
For me this works sometimes while rest of the time I get Error in path.expand(path) : invalid 'path' argument error.Byte
This solution didn't work for me with Rscript dir/foo.R. It did work with R -e "source('dir/foo.R')".Bazar
This does not work for me on MacZolnay
T
85

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.

Tal answered 29/11, 2009 at 15:4 Comment(7)
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!Ramekin
But actually, it does work fine with source! Just source(other.name) and it works properly.Tal
I think maybe we're talking at cross purposes. I think we have different understandings of what the dehmann is interested in doing.Tal
For path concatenation, better to use other.name <- file.path(script.basename, "other.R")Lemuroid
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.Refluent
After all these years I consider this to be the best and cleanest answer.Marrero
In windows/Rstudio it returns character(0)Coopery
T
63

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))
        }
}
Toucan answered 12/3, 2013 at 23:0 Comment(6)
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.Wax
This is the only thing that worked. Note, for this to work library(base) took me a while to figure that out lolSwanky
you sir get my vote, because this is the solution that worked for meIndurate
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.Sawyor
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?Gymno
This does not work for me in RStudio/Windows from sys.frames()[[1]]$ofile I get NULL.Coopery
R
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 :/

Ramekin answered 29/11, 2009 at 19:21 Comment(8)
In what context does that work? print(sys.frames()) turns up NULL when I run it.Tal
@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.Flyte
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.Ramekin
This does not answer my question. I need to automatically find the "other.R" file. x$ofile is undefined, so frame_files is empty.Humankind
@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 reloaderShellieshellproof
I do not see ofile but do see fileNameChadwickchae
This worked for me in windows, in linux, but then it failed when I called it from crontab. SAD!Polytrophic
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.Hydrometallurgy
W
35

This works for me

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
Wassyngton answered 23/8, 2017 at 15:38 Comment(3)
This only works from inside RStudio I guess. Trying from the terminal I get Error: RStudio not running.Parrie
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 caseDivan
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.Perilous
L
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=""))
Limitary answered 29/3, 2016 at 5:39 Comment(7)
For me, your solution was the best. Specially because it could be applied to a Shiny app and that one on link not.Rhapsodic
Here the getSrcDirectory is utils::getSrcDirectoryAniakudo
This might work nicely under Linux/Mac, but it did not work for me in an interative RStudio session under Windows. sourceDir was blank.Milliard
@Milliard on an interactive terminal, there is no path!!! You want the path to a file.Selective
I'm getting character(0). Suggestions?Expectorant
RStudio returns a blank value when I try this on a Linux server in an interactive session.Makepeace
@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.Limitary
I
25

I've made a package for this, available on CRAN and GitHub, called this.path. The current version is 2.4.0 (2024-02-16), 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")

## or:

remotes::install_github("ArcadeAntics/this.path")

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 the following GUIs:

  • compatibility with the following functions and packages:

  • 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:// URLs 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(), such as:

    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 starting 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. argument 'file' is stored as a promise containing the
    ## expression `this.path()`. when '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. otherwise,
    ## the source call is deemed inappropriate and 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 evaluate a promise, R is capable of realizing if a
    ## variable is asking for its own definition (a recursive promise).
    ## The error is "promise already under evaluation" which indicates
    ## that the promise is requesting its own value. So we use the
    ## 'tryCatch' to get '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)) {
        fun <- sys.function(n)
        if (identical(fun, source)) {
            if (!exists("ofile", envir = sys.frame(n), inherits = FALSE))
                next
            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(fun, sys.source)) {
            if (!exists("exprs", envir = sys.frame(n), inherits = FALSE))
                next
            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(fun, debugSource)) {
            threw_error <- tryCatch({
                path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
                FALSE
            }, error = function(c) TRUE)
            if (threw_error)
                next
            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("R is running from RStudio with no documents open\n",
                 " (or 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("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("R is running from a shell and argument 'FILE' is missing")
        }
    }


    ## running from RGui on Windows
    else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {
        stop("R is running from Rgui which is currently unimplemented\n",
             " 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("R is running from AQUA which is currently unimplemented\n",
             " consider using RStudio until such a time when this is implemented")
    }


    ## otherwise
    else stop("R is running in an unrecognized manner")
}
Inhabit answered 30/9, 2020 at 1:18 Comment(11)
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 stackWoodsy
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!Inhabit
@Woodsy 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!Inhabit
@Woodsy 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'!Inhabit
Just tested v.0.2.0 on a script file saved in an RGui session, and it works for me. Thanks!Woodsy
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.Vtol
@Vtol 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!Inhabit
@Bazar 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) :.Vtol
@Vtol Would you mind if we moved this to 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!Inhabit
This should be the right answer, it's the only one that's working, at least for me.Stunt
currently working in R studio console/in-line & sourcedCatcall
M
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() {
    # https://mcmap.net/q/99675/-determine-path-of-the-executing-script
    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
                # https://mcmap.net/q/101731/-getting-path-of-an-r-script
                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)
                }
            }
        }
    }
}
Muddle answered 21/4, 2016 at 18:24 Comment(3)
Doesn't work with interactive R session; I'm getting: ``` > source("csf.R") > csf() Error: RStudio not running ```Supraliminal
This is great. Can someone make a package?Shortstop
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.Perilous
K
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="/"))
}
Kalin answered 23/6, 2011 at 23:31 Comment(1)
This did not work recursively; the file I source looks for a data file (but in the wrong directory).Gyrose
B
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)])))
Brindled answered 28/9, 2011 at 15:26 Comment(2)
This is the correct answer. Really baffling how many people are wasting time with the other proposed answers to this.Cumshaw
This just returned blank value for me in interactive R session.Makepeace
G
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.

Gemini answered 29/11, 2013 at 9:52 Comment(0)
T
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.

Tyrosine answered 13/11, 2018 at 8:26 Comment(1)
Package ‘scriptName’ was removed from the CRAN repository. - what now? :oTyrosine
M
7

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))
        }
    }
}
Mixon answered 14/8, 2015 at 18:46 Comment(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 caseAirel
S
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.

Shanonshanta answered 13/12, 2019 at 15:7 Comment(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"Teddytedeschi
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.Shortstop
R
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(".")
Rejoice answered 5/8, 2016 at 1:5 Comment(4)
Nope. That finds the directory of the process, not the file itself.Internuncio
This worked for me in Windows with RStudio in interactive mode.Milliard
Thank You!. Finally something that works in interactive RStudio sessions in Linux.Makepeace
setwd(".") and (in your case) setwd(wd) are both nonsensical operations. They don’t have any effect.Negatron
K
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
Kickapoo answered 29/11, 2009 at 14:12 Comment(2)
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.Justitia
@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 #60395 path_to_script="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"Dona
B
2

I like this approach:

this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
Burgos answered 16/12, 2014 at 22:43 Comment(0)
T
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.

Thermopylae answered 15/3, 2017 at 21:13 Comment(3)
It always returns NA, even if I create a script that prints its output and then call the script e.g. with R -e "library(getopt); testscript.R"Revile
As the name of the function implies, you need to run your script using Rscript.Thermopylae
Ah, oops. Thanks.Revile
R
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.

Randell answered 29/7, 2017 at 12:37 Comment(0)
C
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.

Crocoite answered 5/1, 2018 at 19:22 Comment(1)
For me the here package do exactly the job and seems to be an easy solutionIrrupt
D
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)
Dopp answered 27/5, 2015 at 6:40 Comment(0)
C
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))
  }
})()
Carter answered 2/9, 2015 at 13:23 Comment(0)
D
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.

Desdee answered 25/11, 2016 at 13:13 Comment(1)
Not from within RStudio, though, except when sourcingChavis
G
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) ))()
Gastrointestinal answered 6/8, 2019 at 16:41 Comment(0)
A
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!

Abney answered 15/3, 2023 at 19:43 Comment(7)
This “solution” was already posted, and the previous answer has several comments explaining why it is not a good solution.Negatron
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).Abney
See https://mcmap.net/q/99675/-determine-path-of-the-executing-script and https://mcmap.net/q/99675/-determine-path-of-the-executing-script.Negatron
so far this is the only solution that works for me. @KonradRudolph why it is not a good solution?Lucilius
@Lucilius Because it does not work. See the links in my previous comment.Negatron
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)Lucilius
@Konrad: I see that I was focused on solutions from within R Studio and neglected the command line or console case. Do you recommend I delete my answer? Or do you feel my clarifying bullets are useful to illustrate a possible solution in those contexts? I'll defer to your judgment. Thanks for the links!Abney
F
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().

Factional answered 18/5, 2014 at 17:2 Comment(0)
H
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='/'))
Horatia answered 23/11, 2014 at 14:35 Comment(1)
I still get the error "Error in sys.frame(1) : not that many frames on the stack "Parrot
E
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")
Elfin answered 13/3, 2018 at 12:27 Comment(1)
I get an error message readLink: illegal option -- e usage: readLink [-FlLnqrsx] [-f format] [-t timefmt] [file ...]Berthoud
C
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
Cavort answered 26/8, 2020 at 4:5 Comment(0)
T
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.

Trematode answered 9/6, 2021 at 9:59 Comment(1)
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.Negatron
F
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')
Formfitting answered 1/12, 2021 at 12:18 Comment(1)
This is an impractical way for something that has better, existing solutions (in particular, box::use()).Negatron

© 2022 - 2024 — McMap. All rights reserved.