R 中的
source()
命令一定有一些我不明白的地方。我对它还是个新手,但我一生都无法理解它是如何获取目录的!我的问题是这样的:
我有一个包装脚本,
wrapper.R
,和一个包含一些函数的源文件,functions.R
。这两个都在同一目录中。如果我在包装器脚本中调用 source('functions.R')
,同时站在两个文件所在的目录中,一切都很好。但是,我希望能够从某个 other目录运行我的
wrapper.R
脚本,i.e. 不是这些脚本所在的目录。如果我为另一个目录运行包装器,它不起作用,并且会出现 cannot open the file
错误。
我用谷歌搜索并发现了很多不同的线程,但是这个问题似乎非常清楚。按照我理解的方式,我正在做的方式应该有效。显然,我误解了一些东西。我对该线程的阅读使我相信
source()
适用于调用 source()
的文件所在的目录。我的阅读还使我相信我不应该按照我的意愿使用 chdir = TRUE
保留公布的相对目录。
看来它不起作用......我误解了什么?当从其他地方调用时,如何将文件源放在与包装器脚本相同的目录中?
您可以使用here包来完成此操作。它使用“加载包时的当前工作目录”。换句话说,您启动 R 会话的目录。
在你的情况下,代码是:
source(here::here('functions.R'))
即使包装器脚本
wrapper.R
位于项目中的不同目录中,这也将起作用。
如果
functions.R
在项目的子目录中,只需将其添加到对here()
的调用中,即可完成相对路径:
source(here::here('subdirectory', 'functions.R'))
如果您要向同事分发脚本,那么您确实不应该编写引用其他脚本的脚本。如果您将来想要重命名或移动
functions.R
怎么办?如果您需要修改 functions.R
中的函数,但 wrapper.R
依赖于该函数的旧版本怎么办?这是一个脆弱的解决方案,会导致头痛。我会推荐以下任一选项。
将所需的所有内容放入一个独立的脚本中并分发。
如果您真的想将代码分离到不同的文件中,请编写一个包。听起来可能有点矫枉过正,但包实际上可以非常简单和轻量级。在最简单的形式中,包只是一个包含
DESCRIPTION
和 NAMESPACE
文件以及 R/
目录的目录。 Hadley 很好地解释了这一点:https://r-pkgs.org/whole-game.html.
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
脚本的文件夹名称相对应。
请参阅入门小插图,了解有关“盒子”模块使用的更多信息。
也许您可以在
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
。
我还没有看到的一个答案是只使用绝对路径。当您
source("myfunctions.R")
时,它使用来自 getwd()
的隐式相对路径。使用完整路径以避免更改工作目录时出现问题。
不过,在共享工作时,其他人必须自己更改所有路径。
我知道有点晚了,但我在获取脚本来源的目录时遇到了这个答案。
我认为您应该能够在不安装额外软件包的情况下执行此操作:
cur_dir = utils::getSrcDirectory(function(){})[1]
source(file.path(cur_dir, 'functions.R'))