我有一个名为foo.R
的脚本,它包括另一个脚本other.R
,两个脚本位于同一个目录中:
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
但是我希望R
能够找到那个other.R
,无论当前的工作目录是什么。
换句话说,foo.R
需要知道它自己的路径。我该如何做到这一点?
我有一个名为foo.R
的脚本,它包括另一个脚本other.R
,两个脚本位于同一个目录中:
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
但是我希望R
能够找到那个other.R
,无论当前的工作目录是什么。
换句话说,foo.R
需要知道它自己的路径。我该如何做到这一点?
由于我的脚本是从符号链接目录操作的,因此我对以上实现存在问题,或者至少我认为以上解决方案对我没有起作用。参考 @ennuikiller 的回答,我将我的 Rscript 包装在 bash 中。我使用 pwd -P
设置路径变量,该命令解析符号链接目录结构。然后将路径传递到 Rscript。
Bash.sh
#!/bin/bash
# set path variable
path=`pwd -P`
#Run Rscript with path argument
Rscript foo.R $path
foo.R
args <- commandArgs(trailingOnly=TRUE)
setwd(args[1])
source(other.R)
sys.calls()[[1]] [[2]]
source(some args, file="myscript")
,它将无法工作。在这些花哨的情况下,请使用@hadley的方法。我发现最灵活的解决方案是使用 rstudioapi::getSourceEditorContext()
和(可选)sub()
请尝试以下操作:
current_file <-
rstudioapi::getSourceEditorContext()$path %>%
sub(".*/", "", .)
rstudioapi::getSourceEditorContext()$path
返回当前文件的完整路径。
sub(".*/", "", .)
提取最后一个 /
后面的所有内容,只留下文件名。
希望这有所帮助!
我会使用@steamer25的方法的变体。重要的是,即使我的会话是通过Rscript启动的,我仍然希望获取最后一个已源代码化的脚本。当包含以下代码片段的文件被调用时,将提供一个名为thisScript
的变量,其中包含了脚本的标准化路径。我承认滥用source函数,所以有时我会调用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))
}
})()
通过查看调用栈,我们可以获得正在执行的每个脚本的文件路径,最有用的两个可能是当前正在执行的脚本或第一个被引用的脚本(入口)。
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) ))()
#!/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='/'))
source
或 sys.source
时才有效,它总是获取堆栈上的第一个 source
,而不是最近的。 - Irissource()
运行脚本或从命令行运行),即使如此,它也不总是有效(R可以使用-f
而不是--file =
来调用)。您不应该依赖它。 - Konrad Rudolph在上面的答案基础上,为了进行安全检查,您可以添加一个包装器,要求用户查找文件,如果(由于某种原因)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')
box::use()
)来说是不切实际的。 - Konrad RudolphfindSourceTraceback()
函数,该函数可以在所有调用帧中查找由source()生成的所有'srcfile'对象。这使得我们能够找出当前由source()脚本化的文件。真是令人惊讶,在R中没有'$0'类型的结构!您可以通过调用一个在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")
readLink: illegal option -- e usage: readLink [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
。 - altabq