R: source() 和源文件路径

R: source() and path to source files

关于 R 中的 source() 命令,肯定有一些我不明白的地方。我对它还是个新手,但我终其一生都无法理解它是如何从中获取目录的!我的问题是:

我有一个包装脚本 wrapper.R 和一个包含一些函数的源文件 functions.R。这两个都在同一个目录中。如果我在包装脚本中调用 source('functions.R'),同时站在两个文件所在的目录中,一切都很好。但是,我希望能够从某个 other 目录 运行 我的 wrapper.R 脚本, 不是那个目录这些脚本位于。如果我 运行 我的另一个目录的包装器,它不起作用,我得到一个 cannot open the file 错误。

我用谷歌搜索并发现了很多不同的线程,但 this question 似乎很清楚。按照我的理解,我的做法 应该可行 。显然,我误会了什么。我对该线程的阅读让我相信 source() 在调用 source() 的文件所在的目录上工作。我的阅读也让我相信我不应该使用 chdir = TRUE,因为我想保留广告的相对目录。

看来它不起作用...我误会了什么?当从其他地方调用时,如何将源文件与我的包装脚本放在同一目录中?

也许您可以在 wrapper.R 中定义一个辅助函数,它会尝试从同一目录加载其他文件。例如

source_here <- function(x, ...) {
    dir <- "."
    if(sys.nframe()>0) {
        frame <- sys.frame(1)
        if (!is.null(frame$ofile)) {
            dir <- dirname(frame$ofile)
        }
    }
    source(file.path(dir, x), ...)
}

然后你会打电话给

# inside wrapper.R
source_here("functions.R")

然后您将只有源 wrapper.R,它将在同一目录中查找 functions.R

如果您要将脚本分发给同事,您真的不应该编写引用其他脚本的脚本。如果您想重命名或移动怎么办functions.R在将来?如果您需要修改 functions.R 中的函数,但 wrapper.R 依赖于该函数的旧版本怎么办?这是一个会引起头痛的脆弱的解决方案。我会推荐以下任何一种。

  1. 将所需的一切放入一个独立的脚本中并分发。

  2. 如果你真的想把代码分成不同的文件,写一个包。听起来可能有点矫枉过正,但包实际上可以非常简单和轻量级。在最简单的形式中,包只是一个包含 DESCRIPTIONNAMESPACE 文件以及 R/ 目录的目录。 Hadley 很好地对此进行了分解:https://r-pkgs.org/whole-game.html.

您可以使用 here 包执行此操作。它使用 "current working directory at the time when the package is loaded"。换句话说,您从中启动 R 会话的目录。

在您的情况下,代码为:

source(here::here('functions.R'))

即使包装脚本 wrapper.R 位于项目中的不同目录中,这仍然有效。

如果functions.R在项目的子目录下,直接添加到here()的调用即可,完成相对路径:

source(here::here('subdirectory', 'functions.R'))

我还没有看到的一个答案是只使用绝对路径。当您 source("myfunctions.R") 时,它使用来自 getwd() 的隐式相对路径。使用完整路径以避免在更改工作目录时出现问题。 但是,共享工作时,其他人必须自己更改所有路径。

source根本不支持这个。其他答案显示了一些在有限情况下有效但在某些(常见)情况下都失败的解决方法。特别是,chdir = TRUE 不是您自己指出的好选择。

更好的解决方案是从“box”包中 box::use。此包允许您将 R 源代码视为适当的 模块 。其中之一 属性 是模块可以加载本地模块。

在您的 wrapper.R 中,将 source 调用替换为

box::use(./functions[...])

或者,如果您想从 wrapper.R 模块中 导出 这些函数(而不是仅在内部使用它们),请改为执行以下操作:

#' @export
box::use(./functions[...])

并加载 wrapper.R 本身,使用

box::use(project/wrapper)

其中project为项目名称,需要与您的wrapper.R脚本所在的文件夹名称相对应。

请查阅 Get started 小插图,了解有关“盒子”模块用法的更多信息。