R:source() 和源文件路径

问题描述 投票:0回答:6

R 中的

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

我有一个包装脚本,

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

我用谷歌搜索并发现了很多不同的线程,但是这个问题似乎非常清楚。按照我理解的方式,我正在做的方式应该有效。显然,我误解了一些东西。我对该线程的阅读使我相信

source()
适用于调用
source()
的文件所在的目录。我的阅读还使我相信我不应该按照我的意愿使用
chdir = TRUE
保留公布的相对目录。

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

r function relative-path chdir
6个回答
29
投票

您可以使用here包来完成此操作。它使用“加载包时的当前工作目录”。换句话说,您启动 R 会话的目录。

在你的情况下,代码是:

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

即使包装器脚本

wrapper.R
位于项目中的不同目录中,这也将起作用。

如果

functions.R
在项目的子目录中,只需将其添加到对
here()
的调用中,即可完成相对路径:

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

21
投票

如果您要向同事分发脚本,那么您确实不应该编写引用其他脚本的脚本。如果您将来想要重命名或移动

functions.R
怎么办?如果您需要修改
functions.R
中的函数,但
wrapper.R
依赖于该函数的旧版本怎么办?这是一个脆弱的解决方案,会导致头痛。我会推荐以下任一选项。

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

  2. 如果您真的想将代码分离到不同的文件中,请编写一个包。听起来可能有点矫枉过正,但包实际上可以非常简单和轻量级。在最简单的形式中,包只是一个包含

    DESCRIPTION
    NAMESPACE
    文件以及
    R/
    目录的目录。 Hadley 很好地解释了这一点:https://r-pkgs.org/whole-game.html.


4
投票

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
脚本的文件夹名称相对应。

请参阅入门小插图,了解有关“盒子”模块使用的更多信息。


3
投票

也许您可以在

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


2
投票

我还没有看到的一个答案是只使用绝对路径。当您

source("myfunctions.R")
时,它使用来自
getwd()
的隐式相对路径。使用完整路径以避免更改工作目录时出现问题。 不过,在共享工作时,其他人必须自己更改所有路径。


0
投票

我知道有点晚了,但我在获取脚本来源的目录时遇到了这个答案

我认为您应该能够在不安装额外软件包的情况下执行此操作:

cur_dir = utils::getSrcDirectory(function(){})[1]
source(file.path(cur_dir, 'functions.R'))
© www.soinside.com 2019 - 2024. All rights reserved.