我有一个名为 foo.R 的脚本,其中包含另一个脚本 other.R ,它位于同一目录中:
foo.R
other.R
#!/usr/bin/env Rscript print("Hello") source("other.R")
但我希望 R 找到 other.R ,无论当前的工作目录是什么 .
R
换句话说, foo.R 需要知道自己的路径 . 我怎样才能做到这一点?
#!/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='/'))
这对我有用 . 只需从命令行参数中获取它,剥离不需要的文本,执行一个dirname,最后从中获取完整路径:
args <- commandArgs(trailingOnly = F) scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
我把这个问题的答案包装起来并扩展到rprojroot中的新函数 thisfile() . 也可用于编织 knitr .
thisfile()
knitr
Here这个问题有一个简单的解决方案 . 这个命令:
script.dir <- dirname(sys.frame(1)$ofile)
返回当前脚本文件的路径 . 保存脚本后它可以工作 .
您可以使用 commandArgs 函数获取Rscript传递给实际R解释器的所有选项,并搜索它们 --file= . 如果您的脚本是从路径启动的,或者是使用完整路径启动的,则下面的 script.name 将以 '/' 开头 . 否则,它必须相对于 cwd ,您可以连接两个路径以获取完整路径 .
commandArgs
--file=
script.name
'/'
cwd
Edit: 听起来你只需要上面的 script.name 并去掉路径的最后一个组件 . 我删除了不需要的 cwd() 示例并清理了主脚本并发布了我的 other.R . 只需将此脚本和 other.R 脚本保存到同一目录中,然后运行主脚本即可 .
cwd()
main.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)
other.R :
print("hello")
output :
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"
这就是我认为德曼正在寻找的东西 .
我喜欢这种方法:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile this.dir <- dirname(this.file)
令人惊讶的是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)
然后只需为other.R拆分scriptpath.sh名称
splitstr <- rev(strsplit(thisscript, "\\/")[[1]]) otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")
请注意,getopt包提供 get_Rscript_filename 函数,它只使用此处提供的相同解决方案,但已在标准R模块中为您编写,因此您无需将"get script path"函数复制并粘贴到您编写的每个脚本中 .
get_Rscript_filename
我尝试了几乎所有这个问题,Getting path of an R script,Get the path of current script,Find location of current .R file和R command for setting working directory to source file location in Rstudio,但最后发现自己手动浏览CRAN表并找到了
scriptName库
它提供 current_filename() 函数,当在RStudio中获取时以及通过R或RScript可执行文件调用时返回脚本的正确完整路径 .
current_filename()
您可以将r脚本包装在bash脚本中,并将脚本的路径检索为bash变量,如下所示:
#!/bin/bash # [environment variables can be set here] path_to_script=$(dirname $0) R --slave<<EOF source("$path_to_script/other.R") EOF
如果不是脚本, foo.R ,知道它的路径位置,如果你可以改变你的代码以始终引用来自公共 root 的所有 source 'd路径,那么这些可能是一个很大的帮助:
root
source
https://krlmlr.github.io/here/
https://krlmlr.github.io/rprojroot/
特定
/app/deeply/nested/foo.R
/app/other.R
这会奏效
#!/usr/bin/env Rscript library(here) source(here("other.R"))
有关如何定义项目根的信息,请参阅https://krlmlr.github.io/rprojroot/ .
我的一体!
#' current script file (in full path) #' @param #' @return #' @examples #' works with Rscript, source() or in RStudio Run selection #' @export 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 return(normalizePath(rstudioapi::getActiveDocumentContext()$path)) } } } }
来自Getting path of an R script的rakensi的答案是最正确和最精彩的恕我直言 . 然而,它在这里引用它,以便让别人更容易找到它 .
sourceDir < - getSrcDirectory(function(dummy))
这给出了放置语句的文件的目录(定义了伪函数的位置) . 然后它可以用于设置工作目标并使用相对路径,例如
setwd(sourceDir) source("other.R")
或创建绝对路径
source(paste(sourceDir, "/other.R", sep=""))
我自己就是这么做的 . 要确保脚本的可移植性,请始终使用以下命令:
wd <- setwd(".") setwd(wd)
它的工作原理是因为"."的翻译类似于Unix命令$ PWD . 将此字符串分配给字符对象允许您将该字符对象插入到setwd()和Presto中,您的代码将始终以其当前目录作为工作目录运行,无论它在哪个机器上,或者在文件结构中它在哪里位于 . (额外的好处:wd对象可以与file.path()一起使用(即file.path(wd,"output_directory"),以允许创建标准输出目录,而不管通往命名目录的文件路径 . 要求您在以这种方式引用之前创建新目录,但也可以使用wd对象进行帮助 .
或者,以下代码执行完全相同的操作:
wd <- getwd() setwd(wd)
或者,如果您不需要对象中的文件路径,您可以简单地:
setwd(".")
这对我有用
library(rstudioapi) rstudioapi::getActiveDocumentContext()$path
frame_files <- lapply(sys.frames(), function(x) x$ofile) frame_files <- Filter(Negate(is.null), frame_files) PATH <- dirname(frame_files[[length(frame_files)]])
不要问我它是如何工作的,因为我忘记了:/
您可能只使用99%的案例:
sys.calls()[[1]] [[2]]
它不适用于脚本不是第一个参数的疯狂调用,即 source(some args, file="myscript") . 在这些奇特的案例中使用@ hadley's .
source(some args, file="myscript")
我会使用@ steamer25方法的变体 . 关键是我更喜欢获取最后一个源脚本,即使我的会话是通过Rscript启动的 . 下列片段,当包含在文件中时,将提供包含脚本的规范化路径的变量 thisScript . 我承认(ab)使用source'ing,所以有时我调用Rscript并且 --file 参数中提供的脚本会提供另一个脚本来源另一个...有一天我会投入使我的杂乱代码变成一个包 .
thisScript
--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)) } })()
Steamer25的方法有效,但前提是路径中没有空格 . 在macOS上,至少 cmdArgs[match] 为 /base/some\ dir\ with\ whitespace/ 返回类似 /base/some~+~dir~+~with~+~whitespace/ 的内容 .
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块 .
我的上述实现存在问题,因为我的脚本是从符号链接的目录中运行的,或者至少对我来说是有效的 . 按照@ ennuikiller的回答,我把我的Rscript包裹在bash中 . 我使用 pwd -P 设置了路径变量,它解析了符号链接的目录结构 . 然后将路径传递给Rscript .
pwd -P
Bash.sh
#!/bin/bash # set path variable path=`pwd -P` #Run Rscript with path argument Rscript foo.R $path
args <- commandArgs(trailingOnly=TRUE) setwd(args[1]) source(other.R)
请参阅R.utils包的 findSourceTraceback()
findSourceTraceback()
在所有调用帧中查找source()生成的所有'srcfile'对象 . 这使得有可能找出source()当前编写的文件 .
Supressingfire的答案的缩小版本:
source_local <- function(fname){ argv <- commandArgs(trailingOnly = FALSE) base_dir <- dirname(substring(argv[grep("--file=", argv)], 8)) source(paste(base_dir, fname, sep="/")) }
当从R控制台_236444进入时,我无法使用_236443的解决方案 .使用Rscript时,我无法解决问题 .
两全其美?
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)) } }
我喜欢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)) } } }
24 回答
这对我有用 . 只需从命令行参数中获取它,剥离不需要的文本,执行一个dirname,最后从中获取完整路径:
我把这个问题的答案包装起来并扩展到rprojroot中的新函数
thisfile()
. 也可用于编织knitr
.Here这个问题有一个简单的解决方案 . 这个命令:
返回当前脚本文件的路径 . 保存脚本后它可以工作 .
您可以使用
commandArgs
函数获取Rscript传递给实际R解释器的所有选项,并搜索它们--file=
. 如果您的脚本是从路径启动的,或者是使用完整路径启动的,则下面的script.name
将以'/'
开头 . 否则,它必须相对于cwd
,您可以连接两个路径以获取完整路径 .Edit: 听起来你只需要上面的
script.name
并去掉路径的最后一个组件 . 我删除了不需要的cwd()
示例并清理了主脚本并发布了我的other.R
. 只需将此脚本和other.R
脚本保存到同一目录中,然后运行主脚本即可 .main.R :
other.R :
output :
这就是我认为德曼正在寻找的东西 .
我喜欢这种方法:
令人惊讶的是R中没有'$ 0'类型的结构!您可以使用system()调用以R编写的bash脚本来执行此操作:
然后只需为other.R拆分scriptpath.sh名称
请注意,getopt包提供
get_Rscript_filename
函数,它只使用此处提供的相同解决方案,但已在标准R模块中为您编写,因此您无需将"get script path"函数复制并粘贴到您编写的每个脚本中 .我尝试了几乎所有这个问题,Getting path of an R script,Get the path of current script,Find location of current .R file和R command for setting working directory to source file location in Rstudio,但最后发现自己手动浏览CRAN表并找到了
scriptName库
它提供
current_filename()
函数,当在RStudio中获取时以及通过R或RScript可执行文件调用时返回脚本的正确完整路径 .您可以将r脚本包装在bash脚本中,并将脚本的路径检索为bash变量,如下所示:
如果不是脚本,
foo.R
,知道它的路径位置,如果你可以改变你的代码以始终引用来自公共root
的所有source
'd路径,那么这些可能是一个很大的帮助:https://krlmlr.github.io/here/
https://krlmlr.github.io/rprojroot/
特定
/app/deeply/nested/foo.R
/app/other.R
这会奏效
有关如何定义项目根的信息,请参阅https://krlmlr.github.io/rprojroot/ .
我的一体!
来自Getting path of an R script的rakensi的答案是最正确和最精彩的恕我直言 . 然而,它在这里引用它,以便让别人更容易找到它 .
这给出了放置语句的文件的目录(定义了伪函数的位置) . 然后它可以用于设置工作目标并使用相对路径,例如
或创建绝对路径
我自己就是这么做的 . 要确保脚本的可移植性,请始终使用以下命令:
它的工作原理是因为"."的翻译类似于Unix命令$ PWD . 将此字符串分配给字符对象允许您将该字符对象插入到setwd()和Presto中,您的代码将始终以其当前目录作为工作目录运行,无论它在哪个机器上,或者在文件结构中它在哪里位于 . (额外的好处:wd对象可以与file.path()一起使用(即file.path(wd,"output_directory"),以允许创建标准输出目录,而不管通往命名目录的文件路径 . 要求您在以这种方式引用之前创建新目录,但也可以使用wd对象进行帮助 .
或者,以下代码执行完全相同的操作:
或者,如果您不需要对象中的文件路径,您可以简单地:
这对我有用
不要问我它是如何工作的,因为我忘记了:/
您可能只使用99%的案例:
它不适用于脚本不是第一个参数的疯狂调用,即
source(some args, file="myscript")
. 在这些奇特的案例中使用@ hadley's .我会使用@ steamer25方法的变体 . 关键是我更喜欢获取最后一个源脚本,即使我的会话是通过Rscript启动的 . 下列片段,当包含在文件中时,将提供包含脚本的规范化路径的变量
thisScript
. 我承认(ab)使用source'ing,所以有时我调用Rscript并且--file
参数中提供的脚本会提供另一个脚本来源另一个...有一天我会投入使我的杂乱代码变成一个包 .Steamer25的方法有效,但前提是路径中没有空格 . 在macOS上,至少
cmdArgs[match]
为/base/some\ dir\ with\ whitespace/
返回类似/base/some~+~dir~+~with~+~whitespace/
的内容 .我通过在返回之前用简单的空格替换“~~”来解决这个问题 .
显然你仍然可以像aprstar那样扩展else块 .
我的上述实现存在问题,因为我的脚本是从符号链接的目录中运行的,或者至少对我来说是有效的 . 按照@ ennuikiller的回答,我把我的Rscript包裹在bash中 . 我使用
pwd -P
设置了路径变量,它解析了符号链接的目录结构 . 然后将路径传递给Rscript .Bash.sh
foo.R
请参阅R.utils包的
findSourceTraceback()
Supressingfire的答案的缩小版本:
当从R控制台_236444进入时,我无法使用_236443的解决方案 .
使用Rscript时,我无法解决问题 .
两全其美?
我喜欢steamer25的解决方案,因为它似乎对我来说最强大 . 但是,在RStudio(在Windows中)进行调试时,路径将无法正确设置 . 原因是如果在RStudio中设置断点,则获取文件使用备用“调试源”命令,该命令将脚本路径设置为稍有不同 . 以下是我目前使用的最终版本,它在调试时考虑了RStudio中的这种替代行为: