确定执行脚本的路径

我有一个名为foo.R的脚本,其中包括另一个脚本other.R,该脚本位于同一目录:

#!/usr/bin/env Rscript
message("Hello")
source("other.R")

但我想让R找到那个other.R,不管当前工作目录是什么。

换句话说,foo.R需要知道自己的路径。我该怎么做呢?

147710 次浏览

你可以在一个bash脚本中包装r脚本,并检索脚本的路径作为bash变量,如下所示:

#!/bin/bash
# [environment variables can be set here]
path_to_script=$(dirname $0)


R --slave<<EOF
source("$path_to_script/other.R")


EOF

你可以使用commandArgs函数来获取Rscript传递给实际R解释器的所有选项,并搜索它们的--file=。如果你的脚本是从路径启动的,或者它是以一个完整的路径启动的,下面的script.name将以'/'开始。否则,它必须是相对于cwd的,你可以连接两个路径来获得完整的路径。

编辑:听起来好像你只需要上面的script.name,并剥离路径的最后一个组件。我已经删除了不需要的cwd()示例,并清理了主脚本,并发布了我的other.R。只需要保存这个脚本和other.R脚本到相同的目录,chmod +x它们,并运行主脚本。

主要。R:

#!/usr/bin/env Rscript
initial.options <- commandArgs(trailingOnly = FALSE)
file.arg.name <- "--file="
script.name <- sub(file.arg.name, "", initial.options[grep(file.arg.name, initial.options)])
script.basename <- dirname(script.name)
other.name <- file.path(script.basename, "other.R")
print(paste("Sourcing",other.name,"from",script.name))
source(other.name)

其他。R:

print("hello")

输出:

burner@firefighter:~$ main.R
[1] "Sourcing /home/burner/bin/other.R from /home/burner/bin/main.R"
[1] "hello"
burner@firefighter:~$ bin/main.R
[1] "Sourcing bin/other.R from bin/main.R"
[1] "hello"
burner@firefighter:~$ cd bin
burner@firefighter:~/bin$ main.R
[1] "Sourcing ./other.R from ./main.R"
[1] "hello"

我相信这就是德曼在找的东西。

frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])

不要问我它是如何工作的,因为我已经忘记了:/

Supressingfire回答的简化版本:

source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}

这对我很有用。只是从命令行参数中greps它,去掉不需要的文本,执行dirname,最后从它获得完整的路径:

args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))

我不能得到Suppressingfire的解决方案工作时'来源'从R控制台 当使用Rscript时,我无法得到hadley的解决方案

两全其美?

thisFile <- function() {
cmdArgs <- commandArgs(trailingOnly = FALSE)
needle <- "--file="
match <- grep(needle, cmdArgs)
if (length(match) > 0) {
# Rscript
return(normalizePath(sub(needle, "", cmdArgs[match])))
} else {
# 'source'd via R console
return(normalizePath(sys.frames()[[1]]$ofile))
}
}

这个问题有一个简单的解决办法。这个命令:

script.dir <- dirname(sys.frame(1)$ofile)

返回当前脚本文件的路径。它在保存脚本后工作。

我已经将这个问题的答案包装并扩展为rprojroot中的新函数thisfile()。也适用于编织与knitr

参见R.utils包的findSourceTraceback()

查找source()在所有调用帧中生成的所有'srcfile'对象。 这样就可以找出source()当前为哪些文件编写了脚本

#!/usr/bin/env Rscript
print("Hello")


# sad workaround but works :(
programDir <- dirname(sys.frame(1)$ofile)
source(paste(programDir,"other.R",sep='/'))
source(paste(programDir,"other-than-other.R",sep='/'))

我喜欢这种方法:

this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)

我对上面的实现有问题,因为我的脚本是从符号链接目录操作的,或者至少这就是为什么我认为上面的解决方案不适合我。按照@ennuikiller的回答,我将Rscript包装在bash中。我使用pwd -P设置路径变量,它解析符号链接目录结构。然后将路径传递给Rscript。

< em > Bash.sh < / em >

#!/bin/bash


# set path variable
path=`pwd -P`


#Run Rscript with path argument
Rscript foo.R $path

< em > foo。R < / em >

args <- commandArgs(trailingOnly=TRUE)
setwd(args[1])
source(other.R)

我喜欢steamer25的解决方案,因为它似乎是最健壮的。然而,当在RStudio中调试时(在windows中),路径不会被正确设置。原因是,如果在RStudio中设置了断点,那么源文件将使用另一个“调试源”命令,该命令将脚本路径设置得稍微不同。下面是我目前正在使用的最终版本,它解释了调试时RStudio中的这种替代行为:

# @return full path to this script
get_script_path <- function() {
cmdArgs = commandArgs(trailingOnly = FALSE)
needle = "--file="
match = grep(needle, cmdArgs)
if (length(match) > 0) {
# Rscript
return(normalizePath(sub(needle, "", cmdArgs[match])))
} else {
ls_vars = ls(sys.frames()[[1]])
if ("fileName" %in% ls_vars) {
# Source'd via RStudio
return(normalizePath(sys.frames()[[1]]$fileName))
} else {
# Source'd via R console
return(normalizePath(sys.frames()[[1]]$ofile))
}
}
}
我会使用@steamer25方法的变体。关键是,我更喜欢获得最后一个源脚本,即使我的会话是通过Rscript启动的。下面的代码段,当包含在文件中时,将提供一个变量thisScript,其中包含脚本的规范化路径。 我承认(ab)使用source'ing,所以有时我调用Rscript和--file参数中提供的脚本来源另一个脚本,该脚本来源另一个脚本…总有一天我会把我杂乱的代码变成一个包
thisScript <- (function() {
lastScriptSourced <- tail(unlist(lapply(sys.frames(), function(env) env$ofile)), 1)


if (is.null(lastScriptSourced)) {
# No script sourced, checking invocation through Rscript
cmdArgs <- commandArgs(trailingOnly = FALSE)
needle <- "--file="
match <- grep(needle, cmdArgs)
if (length(match) > 0) {
return(normalizePath(sub(needle, "", cmdArgs[match]), winslash=.Platform$file.sep, mustWork=TRUE))
}
} else {
# 'source'd via R console
return(normalizePath(lastScriptSourced, winslash=.Platform$file.sep, mustWork=TRUE))
}
})()

在我看来,来自获取R脚本的路径rakensi的答案是最正确和最聪明的。然而,它仍然是一个包含哑函数的黑客。我在这里引用它,是为了让别人更容易发现。

- gettsrcdirectory(函数(dummy) {dummy})

这给出了放置语句的文件的目录(在那里定义了dummy函数)。然后可以使用它来设置工作目录并使用相对路径。

setwd(sourceDir)
source("other.R")

或者创建绝对路径

 source(paste(sourceDir, "/other.R", sep=""))

我的一切都在一个!(—2019年9月1日更新以处理RStudio控制台)

#' current script file (in full path)
#' @description current script file (in full path)
#' @examples
#' works with Rscript, source() or in RStudio Run selection, RStudio Console
#' @export
ez.csf <- function() {
# http://stackoverflow.com/a/32016824/2292993
cmdArgs = commandArgs(trailingOnly = FALSE)
needle = "--file="
match = grep(needle, cmdArgs)
if (length(match) > 0) {
# Rscript via command line
return(normalizePath(sub(needle, "", cmdArgs[match])))
} else {
ls_vars = ls(sys.frames()[[1]])
if ("fileName" %in% ls_vars) {
# Source'd via RStudio
return(normalizePath(sys.frames()[[1]]$fileName))
} else {
if (!is.null(sys.frames()[[1]]$ofile)) {
# Source'd via R console
return(normalizePath(sys.frames()[[1]]$ofile))
} else {
# RStudio Run Selection
# http://stackoverflow.com/a/35842176/2292993
pth = rstudioapi::getActiveDocumentContext()$path
if (pth!='') {
return(normalizePath(pth))
} else {
# RStudio Console
tryCatch({
pth = rstudioapi::getSourceEditorContext()$path
pth = normalizePath(pth)
}, error = function(e) {
# normalizePath('') issues warning/error
pth = ''
}
)
return(pth)
}
}
}
}
}

我自己刚算出来的。为了确保脚本的可移植性,总是以以下开头:

wd <- setwd(".")
setwd(wd)

它可以工作,因为“。”翻译成Unix命令$PWD。将此字符串赋值给一个字符对象允许你将该字符对象插入到setwd()和转眼间中,你的代码将始终以其当前目录作为工作目录运行,无论它在谁的机器上,也无论它位于文件结构的何处。(额外的奖励:wd对象可以与file.path()一起使用。文件。Path (wd, "output_directory"),以允许创建标准输出目录,而不管指向指定目录的文件路径。这确实要求您在以这种方式引用它之前创建新目录,但这也可以通过wd对象进行帮助。

或者,下面的代码执行完全相同的事情:

wd <- getwd()
setwd(wd)

或者,如果你不需要对象中的文件路径,你可以简单地:

setwd(".")

99%的情况你可以简单地使用:

sys.calls()[[1]] [[2]]

对于脚本不是第一个参数的疯狂调用,例如source(some args, file="myscript"),它将不起作用。在这些奇特的情况下使用@hadley's。

请注意,getopt包提供了get_Rscript_filename函数,它只是使用了这里给出的相同解决方案,但已经在标准R模块中为您编写了,因此您不必将“获取脚本路径”函数复制并粘贴到您编写的每个脚本中。

Steamer25的方法是有效的,但前提是路径中没有空白。在macOS上,至少cmdArgs[match]/base/some\ dir\ with\ whitespace/返回类似/base/some~+~dir~+~with~+~whitespace/的东西。

我通过在返回之前用一个简单的空格替换“~+~”来解决这个问题。

thisFile <- function() {
cmdArgs <- commandArgs(trailingOnly = FALSE)
needle <- "--file="
match <- grep(needle, cmdArgs)
if (length(match) > 0) {
# Rscript
path <- cmdArgs[match]
path <- gsub("\\~\\+\\~", " ", path)
return(normalizePath(sub(needle, "", path)))
} else {
# 'source'd via R console
return(normalizePath(sys.frames()[[1]]$ofile))
}
}

显然,你仍然可以像aprstar那样扩展else块。

这对我很有用

library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path

如果不是脚本,foo.R,知道它的路径位置,如果你可以改变你的代码,总是引用公共root中的所有__abc1路径,那么这些可能会有很大的帮助:

鉴于

  • /app/deeply/nested/foo.R
  • /app/other.R

这是可行的

#!/usr/bin/env Rscript
library(here)
source(here("other.R"))

关于如何定义项目根,请参阅https://rprojroot.r-lib.org/

令人惊讶的是R中没有“$0”类型结构!你可以通过system()调用一个用R编写的bash脚本来实现:

write.table(c("readlink -e $0"), file="scriptpath.sh",col=F, row=F, quote=F)
thisscript <- system("sh scriptpath.sh", intern = TRUE)

然后将scriptpath.sh名称拆分为other。R

splitstr <- rev(strsplit(thisscript, "\\/")[[1]])
otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")

我从这个问题中尝试了几乎所有的方法,获取R脚本路径获取当前脚本路径找到当前。r文件的位置R将工作目录设置为Rstudio中的源文件位置,但最后发现自己手动浏览CRAN表并发现

scriptName图书馆

它提供了current_filename()函数,该函数在RStudio中调用脚本时,以及通过R或RScript可执行文件调用时,返回脚本的正确完整路径。

通过查看调用堆栈,我们可以获得正在执行的每个脚本的文件路径,其中最有用的两个可能是当前正在执行的脚本,或者是要获取的第一个脚本(条目)。

script.dir.executing = (function() return( if(length(sys.parents())==1) getwd() else dirname( Filter(is.character,lapply(rev(sys.frames()),function(x) x$ofile))[[1]] ) ))()


script.dir.entry = (function() return( if(length(sys.parents())==1) getwd() else dirname(sys.frame(1)$ofile) ))()

我也有这个问题,上面的解决方案都不适合我。也许是source或类似的东西,但它还不够清楚。

我找到了这个,对我来说很优雅的解决方案:

paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")

其中重要的事情是fileSnapshot(),它提供了关于文件的大量信息。它返回一个包含8个元素的列表。当你选择path作为列表元素时,路径以\\作为分隔符返回,所以剩下的代码只是改变这一点。

我希望这能有所帮助。

我在一个高性能计算集群环境中工作。我在不同的地方开发代码,而不是在生产运行的地方。在开发过程中,我通常从命令行交互式地调用R ( using RStudio)。有很多source("foo.R")在进行。

在生产运行期间,我通常编写一个bash脚本,尝试不同的参数,并在单独的目录中运行每一组参数。bash脚本利用了工作负载管理器(即SLURM)。在这种环境中,设置环境变量很简单。考虑到这一点,下面的解决方案最适合我。

其他。R

my_message <- function(){
return("R is awkward")
}

foo。R

srcpath = Sys.getenv("R_SRC")
# Check if runnning w/o setting R_SRC - presumably done in directory of development, i.e. /path/to/R/code
if(srcpath == ""){
srcpath="./"
}
source(sprintf("%s/other.R", srcpath))
string = my_message()
print(string)

如果从R交互式shell并在/path/to/R/code中运行此操作,只需执行

> source("foo.R")

如果不是从交互式shell运行,也不是从/path/to/R/code运行,首先设置环境变量R_SRC,然后调用Rscript

$ export R_SRC=/path/to/R/code/
$ Rscript /path/to/R/code/foo.R

我为此做了一个包,在CRAN和GitHub上可用,叫做“this.path”。当前版本是1.1.0,发布于2022-12-01,你可以在这里找到它:

https://CRAN.R-project.org/package=this.path

https://github.com/ArcadeAntics/this.path

从CRAN安装:

utils::install.packages("this.path")

或从GitHub使用:

utils::install.packages("this.path", repos = "https://raw.githubusercontent.com/ArcadeAntics/PACKAGES")

然后使用它:

this.path::this.path()

library(this.path)

this.path()

下面的答案是我的原始答案,仅供参考,尽管它的功能比上面可用的最新版本少了很多。改进包括:

  • this.path()现在可以在VSCode中工作,更多信息请参见?this.path.in.VSCode
  • 在类unix的shell下运行R脚本时,处理带有空格的文件名
  • 处理从shell运行R脚本的两种用法(-f file和——file=file)
  • 当使用source和参数chdir = TRUE时正确地归一化路径
  • 使用source处理文件url(即“;file://绝对路径或相对路径”);和“;file:///绝对路径”;)
  • 更好地处理连接,而不是source中的字符串
  • this.pathsource中的url兼容,即:
source("https://host/path/to/file")

如果在文件中使用了this.path,则返回"https://host/path/to/file"。这也适用于以"http://""ftp://""ftps://"开头的URL。举个例子,试试:

source("https://raw.githubusercontent.com/ArcadeAntics/this.path/main/tests/this.path_w_URLs.R")
  • 兼容包测试,特别是testthat::source_fileknitr::knit
  • 引入函数here,类似于here::here,用于指定相对于执行脚本的目录的绝对文件路径
  • 在Windows上,在Rgui中,增加了对list.dirs(system.file(package = "translations"), full.names = FALSE, recursive = FALSE)列出的所有语言的支持
  • 在脚本中第一次调用this.path时,将规范化路径保存在适当的环境中,这样可以更快地在同一脚本中后续使用,并且与工作目录无关。这意味着当在source中使用相对路径或从shell中运行R时,setwd将不再破坏this.path(只要在该脚本中第一次调用this.path后使用setwd)

最初的回答:

我的回答比Jerry T的回答好多了。我发现的问题是,他们通过检查变量ofile是否在堆栈的第一帧中找到来猜测是否进行了source调用。这将不适用于嵌套的源调用,也不适用于来自非全局环境的源调用。另外,顺序是错误的。在检查shell参数之前,我们必须寻找源调用。以下是我的解决方案:

this.path <- function (verbose = getOption("verbose"))
{
# loop through functions that lead here from most recent to
# earliest looking for an appropriate source call (a call to
# function source / / sys.source / / debugSource in RStudio)
#
# an appropriate source call is one in which the file argument has
# been evaluated (forced)
#
# for example, this means `source(this.path())` is an inappropriate
# source call. the argument 'file' is stored as a promise
# containing the expression "this.path()". when the value of 'file'
# is requested, the expression is evaluated at which time there
# should be two functions on the calling stack being 'source' and
# 'this.path'. clearly, you don't want to request the 'file'
# argument from that source call because the value of 'file' is
# under evaluation right now! the trick is to ask if 'file' has
# already been evaluated, the easiest way of which is to ask if a
# variable exists, one which is only created after the expression
# is necessarily evaluated.
#
# if that variable does exist, then argument 'file' has been forced
# and the source call is deemed appropriate. For 'source', the
# filename we want is the variable 'ofile' from that function's
# evaluation environment. For 'sys.source', the filename we want is
# the variable 'file' from that function's evaluation environment.
#
# if that variable does NOT exist, then argument 'file' hasn't been
# forced and the source call is deemed inappropriate. the 'for'
# loop moves to the next function up the calling stack
#
# unfortunately, there is no way to check the argument 'fileName'
# has been forced for 'debugSource' since all the work is done
# internally in C. Instead, we have to use a 'tryCatch' statement.
# When we ask for an object by name using 'get', R is capable of
# realizing if a variable is asking for its own definition (a
# recursive promise). The exact error is "promise already under
# evaluation" which indicates that the promise evaluation is
# requesting its own value. So we use the 'tryCatch' to get the
# argument 'fileName' from the evaluation environment of
# 'debugSource', and if it does not raise an error, then we are
# safe to return that value. If not, the condition returns false
# and the 'for' loop moves to the next function up the calling
# stack




debugSource <- if (.Platform$GUI == "RStudio")
get("debugSource", "tools:rstudio", inherits = FALSE)
for (n in seq.int(to = 1L, by = -1L, length.out = sys.nframe() - 1L)) {
if (identical(sys.function(n), source) &&
exists("ofile", envir = sys.frame(n), inherits = FALSE))
{
path <- get("ofile", envir = sys.frame(n), inherits = FALSE)
if (!is.character(path))
path <- summary.connection(path)$description
if (verbose)
cat("Source: call to function source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), sys.source) &&
exists("exprs", envir = sys.frame(n), inherits = FALSE))
{
path <- get("file", envir = sys.frame(n), inherits = FALSE)
if (verbose)
cat("Source: call to function sys.source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), debugSource) &&
tryCatch({
path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
TRUE
}, error = function(c) FALSE))
{
if (verbose)
cat("Source: call to function debugSource in RStudio\n")
return(normalizePath(path, mustWork = TRUE))
}
}




# if the for loop is passed, no appropriate
#     source call was found up the calling stack
# next, check if the user is running R from a shell
#     on a Windows OS, the GUI is "RTerm"
#     on a Unix    OS, the GUI is "X11"




# if (running R from a shell)
if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" ||  # on Windows
.Platform$OS.type == "unix"    && .Platform$GUI == "X11") {    # under Unix-alikes




# get all shell arguments that start with "--file="
# check the number of shell arguments starting with "--file="
#     in case more or less than one were supplied




args <- commandArgs()


# remove all trailing arguments
args <- args[seq_len(length(args) - length(commandArgs(trailingOnly = TRUE)))]


# remove the first argument, the name of the executable by which this R process was invoked
args <- args[-1L]




path <- grep("^--file=", args, value = TRUE)
if (length(path) == 1L) {
path <- sub("^--file=", "", path)
if (verbose)
cat("Source: shell argument 'FILE'\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (length(path)) {
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from a shell where formal argument 'FILE' matched by multiple actual arguments")
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from a shell where argument 'FILE' is missing")
}




# if (running R from RStudio)
else if (.Platform$GUI == "RStudio") {




# ".rs.api.getActiveDocumentContext" from "tools:rstudio"
# returns a list of information about the document where your
# cursor is located
#
# ".rs.api.getSourceEditorContext" from "tools:rstudio" returns
# a list of information about the document open in the current
# tab
#
# element 'id' is a character string, an identification for the document
# element 'path' is a character string, the path of the document




context <- get(".rs.api.getActiveDocumentContext",
"tools:rstudio", inherits = FALSE)()
active <- adc[["id"]] != "#console"
if (!active) {
context <- get(".rs.api.getSourceEditorContext",
"tools:rstudio", inherits = FALSE)()
if (is.null(context))
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from RStudio with no documents open\n",
"  (or source document has no path)")
}




path <- context[["path"]]
Encoding(path) <- "UTF-8"
if (nzchar(path)) {
if (verbose)
cat(if (active)
"Source: active document in RStudio\n"
else "Source: source document in RStudio\n")
return(normalizePath(path, mustWork = TRUE))
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
if (active)
"* active document in RStudio does not exist"
else "* source document in RStudio does not exist")
}




# if (running R from RGui on Windows)
else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {




# "getWindowsHandles" from "utils" (Windows exclusive) returns
# a list of external pointers containing the windows handles.
# The thing of interest are the names of this list, these are
# the names of the windows belonging to the current R process.
# Since Rgui can have files besides R scripts open (such as
# images), a regular expression is used to subset only windows
# handles with names that start with "R Console" or end with
# " - R Editor". From there, similar checks are done as in the
# above section for 'RStudio'




x <- names(utils::getWindowsHandles(pattern = "^R Console| - R Editor$",
minimized = TRUE))




if (!length(x))
stop("no windows in Rgui; should never happen, please report!")




active <- !startsWith(x[[1L]], "R Console")
if (active)
x <- x[[1L]]
else if (length(x) >= 2L)
x <- x[[2L]]
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from Rgui with no documents open")
if (x == "Untitled - R Editor")
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
if (active)
"* active document in Rgui does not exist"
else "* source document in Rgui does not exist")
path <- sub(" - R Editor$", "", x)
if (verbose)
cat(if (active)
"Source: active document in Rgui\n"
else "Source: source document in Rgui\n")
return(normalizePath(path, mustWork = TRUE))
}




# if (running R from RGui on macOS)
else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from AQUA which is currently unimplemented\n",
"  consider using RStudio until such a time when this is implemented")
}




# otherwise
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run in an unrecognized manner")
}

解决方案在2016年出现。非常感谢作者,Sahil Seth!

凹口github上的funr包提供了sys.script()函数,该函数获取当前脚本的完整路径。它甚至引用了类似的所以文章

因此,解是:

myscript。接待员:

#!/usr/bin/env Rscript
f  <-  funr::sys.script()
show(f)

然后执行命令:

user@somewhere:/home$ Rscript myscript.R

将在命令行输出,例如:

"/home/path/to/myscript.R"

到控制台。

在上面的答案基础上,作为安全检查,你可以添加一个包装器,当sys.frame(1)失败(如果interactive() == TRUE就可能失败),或者源脚本不在主脚本所期望的位置时,它会要求用户找到文件。

fun_path = tryCatch(expr =
{file.path(dirname(sys.frame(1)$ofile), "foo.R")},
error = function(e){'foo.R'}
)
if(!file.exists(fun_path))
{
msg = 'Please select "foo.R"'
# ask user to find data
if(Sys.info()[['sysname']] == 'Windows'){#choose.files is only available on Windows
message('\n\n',msg,'\n\n')
Sys.sleep(0.5)#goes too fast for the user to see the message on some computers
fun_path  = choose.files(
default = file.path(gsub('\\\\', '/', Sys.getenv('USERPROFILE')),#user
'Documents'),
caption = msg
)
}else{
message('\n\n',msg,'\n\n')
Sys.sleep(0.5)#goes too fast for the user to see the message on some computers
fun_path = file.choose(new=F)
}
}
#source the function
source(file = fun_path,
encoding = 'UTF-8')