我有一个名为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
需要知道它自己的路径。我该如何做到这一点?
dirname(sys.frame(1)$ofile)
时会出现此错误。当使用source("other.R")
执行脚本,且dirname(sys.frame(1)$ofile)
在"other.R"中时,它可以正常工作。 - MurtaNULL
的错误。 - PaulcommandArgs
函数获取由Rscript传递给实际R解释器的所有选项,并搜索其中的--file=
。如果您的脚本从路径启动或者是使用完整路径启动的,下面的script.name
将以'/'
开头。否则,它必须相对于cwd
,您可以连接这两个路径以获取完整路径。
编辑:听起来你只需要上面的script.name
并剥离路径的最后一部分。我已经删除了不必要的cwd()
示例并清理了主要脚本,并发布了我的other.R
。只需将此脚本和other.R
脚本保存到同一目录中,chmod +x
它们,然后运行主脚本。
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")
输出:
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"
我认为这就是dehmann正在寻找的东西。
other.name <- file.path(script.basename, "other.R")
。 - JasoncommandArgs(trailingOnly = FALSE)
时,我得到了[1] "RStudio" "--interactive"
。没有关于调用它的目录的信息。 - Paul当我在R控制台中使用“source”时,无法让Suppressingfire的解决方案起作用。
当我使用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))
}
}
Rscript
和 source()
命令中都能使用。我建议对两个版本都使用 normalizePath()
函数,这样可以在两种情况下都给出完整路径。 - wchlibrary(base)
。哈哈 - O.rkafoo.R
中意味着source(file.path(dirname(thisFile()), "other.R"))
。这对我很有效。 - Kimmain.R
,它又source helper.R
,然后调用thisFile()
。它将获取main.R
的路径而不是helper.R
。这里有什么提示吗? - Wassadamosys.frames()[[1]]$ofile
中我得到了NULL
。 - understoreyframe_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])
不过别问我它是如何工作的,因为我已经忘记了 :/
sys.frames
返回调用栈的环境,所以只有从函数中调用时才真正有意义。例如,尝试 foo <- function() {bar <- function() print(sys.frames()); bar()}; foo()
。但我无法理解 @hadley 的代码,因为环境没有 ofile
成员。 - Richie Cottonsource("~/code/test.r")
,PATH
将被设置为 ~/desktop
。如果你只是在顶层评估它,它将返回 NULL。 - hadleyx$ofile
未定义,因此frame_files
为空。 - Frank这对我有效
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
""
。 - Kayrakensi 的回答来自 如何获取 R 脚本的路径 ,在我看来是最正确和真正精彩的。然而,它仍然是一种使用虚拟函数的技巧。我在这里引用它,以便其他人可以更轻松地找到。
sourceDir <- getSrcDirectory(function(dummy) {dummy})
这将给出放置此语句的文件的目录(定义虚拟函数的位置)。然后可以将其用于设置工作目录并使用相对路径。
setwd(sourceDir)
source("other.R")
或者创建绝对路径
source(paste(sourceDir, "/other.R", sep=""))
sourceDir
是空白的。 - Contangocharacter(0)
。有什么建议吗? - abalterhttps://CRAN.R-project.org/package=this.path
https://github.com/ArcadeAntics/this.path
从CRAN安装它:utils::install.packages("this.path")
utils::install.packages("this.path",
repos = "https://raw.githubusercontent.com/ArcadeAntics/PACKAGES")
this.path::this.path()
或者:
library(this.path)
this.path()
compiler::loadcmp()
,box::use()
,knitr::knit()
,plumber::plumb()
,shiny::runApp()
和testthat::source_file()
-f
FILE
和--file=FILE
)source()
时正确规范化路径参数(chdir = TRUE)
source()
中的文件URI,例如source("file:///path/to/file")
和source("file:///C:/path/to/file")
source()
中连接而不是字符字符串的方式source()
中的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")
here()
/ / this.proj()
,类似于here::here()
,用于指定相对于执行脚本目录/ /执行脚本项目根目录的绝对文件路径this.path()
时,将规范化的路径保存在适当的环境中,这样在同一脚本中后续使用时速度更快,并且与工作目录无关。这意味着setwd()
将不再破坏this.path()
(只要在该脚本中首次调用this.path()
之后使用setwd()
)原始答案:
我的答案是对Jerry T的答案的改进。我发现的问题是他们在猜测是否通过检查堆栈上的第一个帧中是否找到变量ofile
来进行source()
调用。这在嵌套的source调用中不起作用,也不适用于从非全局环境进行的source调用。此外,顺序是错误的。我们必须在检查shell参数之前查找source调用。这是我的解决方案:
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, `source(this.path())` is an inappropriate source
## call. argument 'file' is stored as a promise containing the
## expression `this.path()`. when '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. otherwise,
## the source call is deemed inappropriate and 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 evaluate a promise, R is capable of realizing if a
## variable is asking for its own definition (a recursive promise).
## The error is "promise already under evaluation" which indicates
## that the promise is requesting its own value. So we use the
## 'tryCatch' to get '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, "/", 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, "/", 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, "/", TRUE))
}
}
## no appropriate source call was found up the calling stack
## running from RStudio
if (.Platform$GUI == "RStudio") {
## ".rs.api.getSourceEditorContext" from "tools:rstudio"
## returns a list of information about the document open in the
## current tab
##
## element 'path' is a character string, the document's path
context <- get(".rs.api.getSourceEditorContext",
"tools:rstudio", inherits = FALSE)()
if (is.null(context))
stop("R is running from RStudio with no documents open\n",
" (or document has no path)")
path <- context[["path"]]
if (nzchar(path)) {
Encoding(path) <- "UTF-8"
if (verbose)
cat("Source: document in RStudio\n")
return(normalizePath(path, "/", TRUE))
}
else stop("document in RStudio does not exist")
}
## running from a shell
else if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" || ## on Windows
.Platform$OS.type == "unix" && .Platform$GUI == "X11") ## under Unix-alikes
{
argv <- commandArgs()
## remove all trailing arguments
m <- match("--args", argv, 0L)
if (m)
argv <- argv[seq_len(m)]
argv <- argv[-1L]
## get all arguments starting with "--file="
FILE <- argv[startsWith(argv, "--file=")]
## remove "--file=" from the start of each string
FILE <- substring(FILE, 8L)
## remove strings "-"
FILE <- FILE[FILE != "-"]
n <- length(FILE)
if (n) {
FILE <- FILE[[n]]
if (verbose)
cat("Source: shell argument 'FILE'\n")
return(normalizePath(FILE, "/", TRUE))
} else {
stop("R is running from a shell and argument 'FILE' is missing")
}
}
## running from RGui on Windows
else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {
stop("R is running from Rgui which is currently unimplemented\n",
" consider using RStudio until such a time when this is implemented")
}
## running from RGui on macOS
else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {
stop("R is running from AQUA which is currently unimplemented\n",
" consider using RStudio until such a time when this is implemented")
}
## otherwise
else stop("R is running in an unrecognized manner")
}
Error in this.path() : 'this.path' used in an inappropriate fashion. * no appropriate source call was found up the calling stack. * R is being run from a shell where argument 'FILE' is missing
. - coipError in this.path():
现在变成了Error in this.path(verbose):
。 - coip我的全能工具!(--01/09/2019更新以处理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() {
# https://dev59.com/k3I-5IYBdhLWcg3whIqk#32016824
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
# https://dev59.com/fXA75IYBdhLWcg3wJFcL#35842176
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)
}
}
}
}
}
> source("csf.R")
> csf()
Error: RStudio 未运行
- ManicMailmanSupressingfire的答案进行了精简:
source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}
args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))