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
依赖于该函数的旧版本怎么办?这是一个会引起头痛的脆弱的解决方案。我会推荐以下任何一种。
将所需的一切放入一个独立的脚本中并分发。
如果你真的想把代码分成不同的文件,写一个包。听起来可能有点矫枉过正,但包实际上可以非常简单和轻量级。在最简单的形式中,包只是一个包含 DESCRIPTION
和 NAMESPACE
文件以及 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 小插图,了解有关“盒子”模块用法的更多信息。
关于 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
依赖于该函数的旧版本怎么办?这是一个会引起头痛的脆弱的解决方案。我会推荐以下任何一种。
将所需的一切放入一个独立的脚本中并分发。
如果你真的想把代码分成不同的文件,写一个包。听起来可能有点矫枉过正,但包实际上可以非常简单和轻量级。在最简单的形式中,包只是一个包含
DESCRIPTION
和NAMESPACE
文件以及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 小插图,了解有关“盒子”模块用法的更多信息。