确定正在执行的脚本的路径

300

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

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

但是我希望R能够找到那个other.R,无论当前的工作目录是什么。

换句话说,foo.R需要知道它自己的路径。我该如何做到这一点?


2
没有 :( 我还没有看到任何真正有效的解决方案。除了通过传递目录或使用环境变量的变通方法。 - Frank
4
让脚本完全可移植且甚至 R 初学者也能执行,这将是非常惊人的! - Etienne Low-Décarie
5
似乎所有答案都需要你在某个时刻输入路径(至少要定位文件)!如果你能发送一个压缩文件夹,让任何运行该文件夹内的R脚本文件都从中读取并保存到其中,那将是很好的。 - Etienne Low-Décarie
17
这个单一问题实际上可能成为我完全转向Python的原因。 - Giacomo
9
@giac_man,我觉得R充满了成百上千个这样微小的问题,它们相互累加,使得工作变得非常困难。 - Michael Barton
显示剩余5条评论
31个回答

10

我已经将这个问题的答案打包并扩展到一个新的函数thisfile()中,在rprojroot中。同样适用于使用knitr进行编织。


8

2
Package ‘scriptName’已从CRAN存储库中删除。- 现在怎么办? :o - Bojan P.
1
我已经将名为“this.path”的软件包上传到CRAN,它应该解决这个问题! - Iris

7

我喜欢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))
        }
    }
}

1
Rstudio中的源代码为我提供了ofile,但debugSource却提供了fileName,所以你的解决方案很好,但我的代码注释不太对。 - Mark Adamson

4
我也遇到了这个问题,但以上解决方案都不适用于我。也许是源代码或类似的东西,但不够清晰明了。 对于我来说,我找到了一种优雅的解决方案:
paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")

重要的是 fileSnapshot() 能提供很多关于文件的信息。它返回一个包含 8 个元素的列表。当您选择 path 作为列表元素时,路径将以 \\ 作为分隔符返回,因此代码的其余部分只需更改此处即可。
希望这可以帮助您。

2
这在我的Linux机器上没有起作用;它没有返回文件的路径,而是返回了我当前所在的目录。我创建了一个名为TEST.R的测试脚本,其中只有一行代码:print(fileSnapshot()$path)我将其保存在此文件夹中:/opt/home/boops/Desktop/Testfolder/TEST.R然后我导航到我的桌面并尝试运行该文件:boops@linuxserver:~/Desktop$ Rscript /opt/home/boops/Desktop/Testfolder/TEST.R [1] "/opt/home/boops/Desktop" - Boops Boops
1
对我也没用。在使用'here'库时,它返回与'here()'相同的内容。它返回当前打开的R项目的路径,但不是正在执行的文件本身。 - Joe Flack
fileSnapshot()$path 只返回当前工作目录的路径,而不是执行脚本的路径。另外,在 Windows 上用正斜杠替换反斜杠是不必要的,但在类 Unix 的操作系统上这样做是危险的,因为文件名可能包含反斜杠。最后一件事,您不应该在路径末尾加上路径分隔符,因为 R 将不再将该字符串识别为路径(如 'file.exists' 返回的)。 - Iris

3

我自己解决了这个问题。为了确保您的脚本可移植性,请始终以以下方式开始:

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

它之所以有效,是因为 "." 的翻译类似于 Unix 命令 $PWD。将此字符串分配给字符对象后,您可以将该字符对象插入 setwd() 中,然后您的代码将始终以其当前目录作为工作目录运行,无论它在谁的计算机上或位于文件结构中的何处。(额外奖励:wd 对象可用于 file.path()(即 file.path(wd, "output_directory")),以允许创建标准输出目录,而不管通向命名目录的文件路径如何。这确实需要您在引用它之前创建新目录,但使用 wd 对象也可以帮助您完成这一点。

或者,以下代码执行完全相同的操作:

wd <- getwd()
setwd(wd)

或者,如果你不需要在对象中使用文件路径,你可以简单地:

setwd(".")

13
不行,这会找到进程的目录,而不是文件本身。 - user1071847
这个方法在Windows上使用RStudio交互模式对我有效。 - Contango
谢谢!终于有些东西可以在Linux的交互式RStudio会话中工作了。 - Ryan Howard
setwd(".")和(在您的情况下)setwd(wd)都是毫无意义的操作。它们没有任何效果。 - Konrad Rudolph

2

Steamer25的方法有效,但仅当路径中没有空格时才有效。至少在macOS上,cmdArgs [match] 返回类似于 /base/some~+~dir~+~with~+~whitespace/ 的内容,而针对 /base/some\ dir\ with\ whitespace/

我通过将"~+~"替换为普通空格来解决了这个问题。

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))
  }
}

显然,您仍然可以像aprstar一样扩展else块。

2

对我来说,here包完全胜任工作,似乎是一个简单的解决方案。 - Ron

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

4
这需要你拥有脚本路径。但它并不能让你创建一个真正可移植的 R 脚本,能够在任何地方运行。 - Etienne Low-Décarie
@EtienneLow-Décarie 它不需要脚本路径,它从 bash 中获取。主要问题是这不是一种可靠的获取路径的方式。最好使用类似于 https://dev59.com/yXVD5IYBdhLWcg3wL4cA 的方法。path_to_script="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - John Haberstroh

2
我喜欢这种方法:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)

2
请注意,getopt包提供了get_Rscript_filename函数,该函数仅使用此处介绍的相同解决方案,但已在标准R模块中为您编写,因此您无需将“获取脚本路径”函数复制并粘贴到您编写的每个脚本中。

即使我创建一个打印输出的脚本并使用 R -e "library(getopt); testscript.R" 调用该脚本,它仍然返回 NA。 - bokov
1
如函数名称所示,您需要使用“Rscript”来运行脚本。 - Ryan C. Thompson
啊,糟糕。谢谢。 - bokov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接