我必须对R中的source()
命令有所了解。我还不熟悉它,但我不能为我的生活理解它是如何得到它的目录的从!我的问题是:
我有一个包装器脚本wrapper.R
和一个包含一些函数functions.R
的源文件。这两个都在同一个目录中。如果我在包装器脚本中调用source('functions.R')
,当站在两个文件所在的目录中时,一切都很好。但是,我希望能够从某些其他目录运行我的wrapper.R
脚本,即。而不是这些脚本所在的目录。如果我为另一个目录运行我的包装器,它不起作用,我收到cannot open the file
错误。
我用Google搜索并找到了许多不同的主题,但this question似乎非常清楚。我理解它的方式,我做它的方式应该可以工作。显然,我误解了一些事情。我对该主题的阅读使我相信source()
适用于调用source()
的文件所在的目录。我的阅读也让我相信我不应该使用{{1因为我想保留广告的相对目录。
看到它不起作用......我有什么误解?当从其他地方调用时,如何从与我的包装器脚本相同的目录中获取文件?
答案 0 :(得分:11)
如果您要向同事分发脚本,那么您真的不应该编写源自其他脚本的脚本。如果您希望将来重命名或移动functions.R
该怎么办?如果您需要修改functions.R
中的函数,但wrapper.R
依赖于该函数的旧版本,该怎么办?这是一个脆弱的解决方案,会导致头痛。我会推荐以下任何一种。
将所需的所有内容放入一个自包含的脚本中并分发。
如果确实想要将代码分成不同的文件,请编写一个包。可能听起来有点矫枉过正,但包装实际上可以非常简单轻巧。在最简单的形式中,包只是一个带有DESCRIPTION
和NAMESPACE
文件以及R/
目录的目录。哈德利很好地打破了这个问题:http://r-pkgs.had.co.nz/。
答案 1 :(得分:4)
您可以使用here包执行此操作。它使用“加载包时的当前工作目录”。换句话说,您从。
开始R会话的目录在您的情况下,代码将是:
source(here::here('functions.R'))
即使包装脚本wrapper.R
位于项目的不同目录中,这也会有效。
如果functions.R
位于项目的子目录中,只需将其添加到here()
的调用中,即可完成相对路径:
source(here::here('subdirectory', 'functions.R'))
答案 2 :(得分:2)
也许您可以在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
。
答案 3 :(得分:2)
我尚未看到的一个答案是仅使用绝对路径。 source("myfunctions.R")
时,它使用的是getwd()
中的隐式相对路径。更改工作目录时,请使用完整路径来避免出现问题。
答案 4 :(得分:2)
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 小插图以了解有关“box”模块用法的更多信息。