Determine path of the executing script

前端 未结 28 2468
再見小時候
再見小時候 2020-11-22 08:01

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

#!/usr/bin/env Rscript
message(\         


        
相关标签:
28条回答
  • 2020-11-22 08:21

    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(".")
    
    0 讨论(0)
  • 2020-11-22 08:22

    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)])))
    
    0 讨论(0)
  • 2020-11-22 08:23

    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
    
    0 讨论(0)
  • 2020-11-22 08:23

    I've made a package for this as of 2020-11-11 available on CRAN called "this.path".

    Install it using:

    install.packages("this.path")

    and then use it by:

    this.path::this.path()

    or

    library(this.path)

    this.path()

    I still have my original answer below, though it is less functional than the version in the package. The version in the package accounts for 'sys.source' (a variant of 'source' not commonly used), for the command-line 'Rscript' argument 'file' character conversion on Unix-alike OS, and does not rely on package "rstudioapi". On a Unix-alike OS, strange characters (such as " ") in the command-line 'Rscript' argument 'file' are replaced (such as "~+~"). This makes it so that it is not necessary to use quotation marks to surround that filename for other system commands. Given that we want a path usable in R, any such strange sequences must be replaced.

    Original Answer:

    My answer is an improvement upon Jerry T's answer. The issue I found is that he is 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, a source call must be looked for before checking the command-line arguments, that has also been fixed. Here is my solution:

    this.path <- function (verbose = getOption("verbose"))
    {
        where <- function(x) if (verbose) cat(x, "\n", sep = "")
        # loop through functions that lead here from most recent to
        # earliest (excluding this.path) looking for source
        for (n in seq.int(sys.nframe(), 1L)[-1L]) {
            if (identical(sys.function(n), base::source) &&
                exists("ofile", envir = sys.frame(n), inherits = FALSE)) {
                where("Source: call to function source")
                path <- get("ofile", envir = sys.frame(n), inherits = FALSE)
                if (!is.character(path))
                    path <- summary.connection(path)[["description"]]
                return(normalizePath(path, mustWork = TRUE))
            }
        }
        # if the for loop is passed, no appropriate source call was found
        # next, check if the user is running a file from the command-line
        # get command-line arguments that start with --file=, remove --file= from the start
        if (length(path <- sub("^--file=", "", grep("^--file=", commandArgs(), value = TRUE))))
            where("Source: Command-line argument")
        else if (!requireNamespace("rstudioapi", quietly = TRUE)) {
            where("Unavailable: Package 'rstudioapi' is not installed")
            return("")
        }
        else if (!rstudioapi::isAvailable()) {
            where("Unavailable: RStudio not running")
            return("")
        }
        else if (nzchar(path <- rstudioapi::getActiveDocumentContext()$path))
            where("Source: RStudio Run Selection")
        else if (!is.null(path <- rstudioapi::getSourceEditorContext()$path))
            where("Source: RStudio Console")
        else {
            where("Unavailable: No open R Script")
            return("")
        }
        normalizePath(path, mustWork = TRUE)
    }
    
    0 讨论(0)
  • 2020-11-22 08:24

    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:

    • https://github.com/r-lib/rprojroot or https://rprojroot.r-lib.org/
    • https://here.r-lib.org/

    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.

    0 讨论(0)
  • 2020-11-22 08:25

    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)
                    }
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题