Determine path of the executing script

前端 未结 28 2531
再見小時候
再見小時候 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条回答
  •  -上瘾入骨i
    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)
    }
    

提交回复
热议问题