Rscript:确定执行脚本的路径

Fra*_*ank 234 r file path rscript r-faq

我有一个名为的脚本foo.R包含另一个脚本other.R,该脚本位于同一目录中:

#!/usr/bin/env Rscript
message("Hello")
source("other.R")
Run Code Online (Sandbox Code Playgroud)

但我想R发现other.R无论当前的工作目录是什么.

换句话说,foo.R需要知道自己的路径.我怎样才能做到这一点?

thi*_*ick 93

这里有一个简单的解决方案.这个命令:

script.dir <- dirname(sys.frame(1)$ofile)
Run Code Online (Sandbox Code Playgroud)

返回当前脚本文件的路径.保存脚本后它可以工作.

  • `sys.frame(1)中的错误:堆栈中没有那么多帧 (147认同)
  • 当您尝试直接从Rstudio执行`dirname(sys.frame(1)$ ofile)`时会发生此错误.使用source("other.R")执行脚本时可以正常工作,而``dirname(sys.frame(1)$ ofile)`在`"other.R"`里面. (21认同)
  • 它对我不起作用.我在Windows中运行R. 任何的想法? (4认同)
  • 得到了同样的错误,使用保存的scriptt并重新安装并在Windows上运行R 3.2.0 ... (4认同)
  • 当使用rscript.exe作为脚本调用时,我得到了'堆栈中没有那么多帧'错误,即不使用source().所以我不得不使用下面的Suppressingfire解决方案 (4认同)
  • 当使用闪亮时将它放在server.R中时,我会凝胶`NULL` (2认同)

Sup*_*ire 67

您可以使用该commandArgs函数获取Rscript传递给实际R解释器的所有选项并搜索它们--file=.如果您的脚本是从路径启动的,或者它是以完整路径script.name启动的,则下面的内容将以a开头'/'.否则,它必须相对于cwd,您可以连接两个路径以获取完整路径.

编辑:听起来你只需要script.name上面的内容并去除路径的最后一个组成部分.我删除了不需要的cwd()样本并清理了主脚本并发布了我的other.R.只需将此脚本和other.R脚本保存到同一目录中chmod +x,然后运行主脚本即可.

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)
Run Code Online (Sandbox Code Playgroud)

其他.R:

print("hello")
Run Code Online (Sandbox Code Playgroud)

输出:

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"
Run Code Online (Sandbox Code Playgroud)

这就是我认为德曼正在寻找的东西.

  • 对于路径连接,最好使用`other.name < - file.path(script.basename,"other.R")` (3认同)
  • 我降级是因为您的技术无法像OP所希望的那样与`source`配合使用-但也许我误解了他/她的要求。但我不能取消下调:(对不起! (2认同)
  • 当我尝试在闪亮的应用程序中的 server.R 内运行 `commandArgs(trailingOnly = FALSE)` 时,我得到 `[1] "RStudio" "--interactive"`。没有有关调用它的目录的信息。 (2认同)

ste*_*r25 55

当从R控制台'源'时,我无法让Suppressingfire的解决方案工作.
使用Rscript时,我无法让hadley的解决方案工作.

两全其美?

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))
        }
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个,因为它适用于R中的`Rscript`和`source()`.我建议在两个版本上都使用`normalizePath()`,这样它就可以在两种情况下都提供完整的路径. (5认同)
  • 先生,请您投票,因为这是对我有用的解决方案 (2认同)
  • 如果这对任何人有帮助,对于原始帖子,这将意味着 `foo.R` 中的 `source(file.path(dirname(thisFile()), "other.R"))`。这对我有用。 (2认同)

had*_*ley 35

frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])
Run Code Online (Sandbox Code Playgroud)

不要问我它是如何工作的,因为我忘记了:/

  • 这不回答我的问题.我需要自动找到"other.R"文件.`x $ ofile`未定义,因此`frame_files`为空. (3认同)
  • 在什么情况下有效?我运行时print(sys.frames())变成NULL。 (2认同)

cuf*_*fel 24

来自R脚本的获取路径rakensi的答案是最正确和非常精彩的恕我直言.然而,它仍然是一个包含虚拟功能的黑客.我在这里引用它,以便让别人更容易找到它.

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

这给出了放置语句的文件的目录(定义了伪函数的位置).然后它可以用于设置工作目标并使用相对路径,例如

setwd(sourceDir)
source("other.R")
Run Code Online (Sandbox Code Playgroud)

或创建绝对路径

 source(paste(sourceDir, "/other.R", sep=""))
Run Code Online (Sandbox Code Playgroud)

  • 这可能在Linux/Mac下运行良好,但在Windows下的交互式RStudio会话中它不起作用.`sourceDir`是空白的. (4认同)
  • 我收到“字符(0)”。建议? (3认同)
  • 对我来说,你的解决方案是最好的。特别是因为它可以应用于 Shiny 应用程序,而不是链接上的应用程序。 (2认同)
  • @Contango 在交互式终端上,没有路径!您想要文件的路径。 (2认同)

小智 21

这适合我

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
Run Code Online (Sandbox Code Playgroud)

  • 我猜这只能在RStudio内部使用。从终端尝试,出现“错误:RStudio没有运行”。 (3认同)

mom*_*ara 13

Supressingfire的答案的缩小版本:

source_local <- function(fname){
    argv <- commandArgs(trailingOnly = FALSE)
    base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
    source(paste(base_dir, fname, sep="/"))
}
Run Code Online (Sandbox Code Playgroud)


Jer*_*y T 12

我的一体!( - 01/09/2011更新以处理RStudio控制台)

#' 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)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @JoeFlack我为此制作了一个名为“this.path”的包,您可以在CRAN上找到它 https://CRAN.R-project.org/package=this.path (3认同)

小智 9

这适合我.只需从命令行参数中获取它,剥离不需要的文本,执行一个dirname,最后从中获取完整路径:

args <- commandArgs(trailingOnly = F)  
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
Run Code Online (Sandbox Code Playgroud)


krl*_*mlr 8

我把这个问题的答案包含起来并扩展到rprojroot中的一个新函数thisfile()中.也适用于针织.knitr


And*_*rew 8

截至 2020 年 11 月 11 日,我在 CRAN 上为此制作了一个名为“this.path”的软件包(截至 2021 年 3 月 21 日,最新版本为 0.4.4)。

使用以下方法安装它:

install.packages("this.path")

然后通过以下方式使用它:

this.path::this.path()

或者

图书馆(this.path)

this.path()

下面的答案是我的原始答案,仅供参考,尽管它的功能比 CRAN 上可用的最新版本要少得多。改进包括:

  • 从 Unix 终端运行时处理带空格的文件名
  • 处理从 Windows 命令行运行 R 脚本的两种用途 // Unix 终端(-f file 和 --file=file)
  • 使用base::source参数chdir设置为时正确规范化路径TRUE
  • 处理文件 URL base::source(即“文件://绝对或相对路径”和“文件:///绝对路径”)
  • 更好地处理连接而不是其中的字符串 base::source
  • 与包 testthat 的兼容性,尤其是与 testthat::source_file
  • this.path在脚本中第一次调用时将规范化路径保存在其适当的环境中,从而可以更快地在同一脚本中使用后续时间并且独立于工作目录。这意味着在使用相对路径或从 Windows 命令行运行 R时setwd不会再中断// Unix 终端(只要在第一次调用该脚本后使用)this.pathbase::sourcesetwdthis.path

原答案:

我的回答是对 Jerry T 回答的改进。我发现的问题是他们source通过检查是否ofile在堆栈的第一帧中找到变量来猜测是否进行了调用。这不适用于嵌套的源调用,也不适用于从非全局环境进行的源调用。此外,顺序是错误的。我们必须在检查命令行参数之前查找源调用。这是我的解决方案:

this.path <- function (verbose = getOption("verbose"))
{
    where <- function(x) if (verbose)
        cat("Source: ", x, "\n", sep = "")


    # loop through functions that lead here from most recent to earliest looking
    #     for an appropriate source call (a call to function base::source or base::sys.source)
    # an appropriate source call is a source call in which
    #     argument 'file' has been evaluated (forced)
    # this means, for example, the following is an inappropriate source call:
    #     source(this.path())
    # the argument 'file' is stored as a promise
    #     containing the expression "this.path()"
    # when the value of 'file' is requested, it assigns the value
    #     returned by evaluating "this.path()" to variable 'file'
    # there are two functions on the calling stack at
    #     this point 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 variable ('ofile' for base::source, 'exprs' for base::sys.source)
    #     exists in that function's evaluation environment. this is because that
    #     variable is created AFTER argument 'file' has been forced
    # if that variable does exist, then argument 'file' has been forced and the
    #     source call is deemed appropriate. For base::source, the filename we want
    #     is the variable 'ofile' from that function's evaluation environment. For
    #     base::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 (if available)
    #
    # 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 definition). 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 (if available)


    if (.Platform$GUI == "RStudio")
        dbs <- get("debugSource", mode = "function", "tools:rstudio",
            inherits = FALSE)
    for (n in seq.int(sys.nframe(), 1L)[-1L]) {
        if (identical(sys.function(n), base::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
            where("call to function source")
            return(normalizePath(path, mustWork = TRUE))
        }
        else if (identical(sys.function(n), base::sys.source) &&
            exists("exprs", envir = sys.frame(n), inherits = FALSE)) {
            path <- get("file", envir = sys.frame(n), inherits = FALSE)
            where("call to function sys.source")
            return(normalizePath(path, mustWork = TRUE))
        }
        else if (.Platform$GUI == "RStudio" && identical(sys.function(n), dbs) &&
            tryCatch({
                path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
                TRUE
            }, error = function(c) FALSE)) {
            where("call to function debugSource in RStudio")
            return(normalizePath(path, mustWork = TRUE))
        }
    }


    # if the for loop is passed, no appropriate
    #     source call was found up the calling stack
    # next, check if the user is running R from the command-line
    #     on a Windows OS, the GUI is "RTerm"
    #     on a Unix    OS, the GUI is "X11"


    if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" ||  # running from Windows command-line
        .Platform$OS.type == "unix" && .Platform$GUI == "X11") {       # running from Unix command-line


        # get all command-line arguments that start with "--file="
        # check the number of command-line arguments starting with "--file="
        #     in case more or less than one were supplied


        path <- grep("^--file=", commandArgs(), value = TRUE)
        if (length(path) == 1L) {
            path <- sub("^--file=", "", path)
            where("Command-line argument 'FILE'")
            return(normalizePath(path, mustWork = TRUE))
        }
        else if (length(path)) {
            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 the command-line where formal argument 'FILE' matched by multiple actual arguments")
        }
        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 the command-line where argument 'FILE' is missing")
    }
    else if (.Platform$GUI == "RStudio") {  # running R from 'RStudio'


        # function ".rs.api.getActiveDocumentContext" from the environment "tools:rstudio"
        #     returns a list of information about the document where your cursor is located
        #
        # function ".rs.api.getSourceEditorContext" from the environment "tools:rstudio"
        #     returns a list of information about the document open in the current tab
        #
        # element 'id' is a character string, an identification for the document
        # element 'path' is a character string, the path of the document


        adc <- get(".rs.api.getActiveDocumentContext",
            mode = "function", "tools:rstudio", inherits = FALSE)()
        if (adc$id != "#console") {
            path <- adc$path
            if (nzchar(path)) {
                where("active document in RStudio")
                return(normalizePath(path, mustWork = TRUE))
            }
            else stop("'this.path' used in an inappropriate fashion\n",
                "* no appropriate source call was found up the calling stack\n",
                "* active document in RStudio does not exist")
        }


        sec <- get(".rs.api.getSourceEditorContext", mode = "function",
            "tools:rstudio", inherits = FALSE)()
        if (!is.null(sec)) {
            path <- sec$path
            if (nzchar(path)) {
                where("source document in RStudio")
                return(normalizePath(path, mustWork = TRUE))
            }
            else stop("'this.path' used in an inappropriate fashion\n",
                "* no appropriate source call was found up the calling stack\n",
                "* source document in RStudio does not exist")
        }
        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 RStudio with no documents open")
    }
    else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {  # running R from 'RGui' on Windows


        # on a Windows OS only, the function "getWindowsHandles" from the base
        # package "utils" returns a list of external pointers containing the windows
        # handles. The thing of interest are the names of this list, these should
        # be the names of the windows belonging to the current R process. Since
        # RGui can have files besides R scripts open (such as images), a regular
        # expression is used to subset only windows handles with names that exactly
        # match the string "R Console" or end with " - R Editor". I highly suggest
        # that you NEVER end a document's filename with " - R Editor". From there,
        # similar checks are done as in the above section for 'RStudio'


        wh <- names(utils::getWindowsHandles(pattern = "^R Console$| - R Editor$",
            minimized = TRUE))


        if (!length(wh))
            stop("no windows in RGui; should never happen, please report!")


        path <- wh[1L]
        if (path != "R Console") {
            path <- sub(" - R Editor$", "", path)
            if (path != "Untitled") {
                where("active document in RGui")
                return(normalizePath(path, mustWork = TRUE))
            }
            else stop("'this.path' used in an inappropriate fashion\n",
                "* no appropriate source call was found up the calling stack\n",
                "* active document in RGui does not exist")
        }


        path <- wh[2L]
        if (!is.na(path)) {
            path <- sub(" - R Editor$", "", path)
            if (path != "Untitled") {
                where("source document in RGui")
                return(normalizePath(path, mustWork = TRUE))
            }
            else stop("'this.path' used in an inappropriate fashion\n",
                "* no appropriate source call was found up the calling stack\n",
                "* source document in RGui does not exist")
        }
        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 RGui with no documents open")
    }
    else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {  # running R from 'RGui' on Unix
        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 requires a source call on the calling stack")
    }
    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")
}
Run Code Online (Sandbox Code Playgroud)

  • 刚刚在 RGUI 会话中保存的脚本文件上测试了 v.0.2.0,它对我有用。谢谢! (2认同)

小智 6

我喜欢steamer25的解决方案,因为它似乎对我来说最强大.但是,在RStudio(在Windows中)进行调试时,路径将无法正确设置.原因是如果在RStudio中设置断点,则获取文件使用备用"调试源"命令,该命令将脚本路径设置为略有不同.以下是我目前使用的最终版本,它在调试时考虑了RStudio中的这种替代行为:

# @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))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Boj*_* P. 6

我从这个问题尝试了几乎所有方法,获取R脚本的路径获取当前脚本的路径查找当前.R文件的位置以及用于在Rstudio中将工作目录设置为源文件位置的R命令,但最后还是手动找到了自己浏览CRAN表并找到

scriptName 图书馆

它提供current_filename()功能,当在RStudio中进行采购以及通过R或RScript可执行文件进行调用时,该函数将返回脚本的正确完整路径。

  • `包 'scriptName' 已从 CRAN 存储库中删除。` - 现在怎么办?:o (2认同)

enn*_*ler 2

您可以将 r 脚本包装在 bash 脚本中,并将脚本的路径作为 bash 变量检索,如下所示:

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

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

     EOF
Run Code Online (Sandbox Code Playgroud)

  • 这需要您有脚本路径。它不允许您创建可以在任何地方运行的真正可移植的 R 脚本。 (4认同)