获取当前脚本的路径

71
我想通过编程方式将工作目录设置为当前脚本的路径,但首先需要获取当前脚本的路径。
所以我希望能够做到:
current_path = ...retrieve the path of current script ...
setwd(current_path) 

就像RStudio菜单一样: {{link1:RStudio设置工作目录}}
到目前为止,我尝试了:
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)
返回 NULL。
source("script.R", chdir = TRUE)
返回值:文件(filename, "r", encoding = encoding)中出现错误:无法建立连接。此外,警告信息如下:在文件(filename, "r", encoding = encoding)中有一个警告:无法打开文件 '/script.R':没有这样的文件或目录。
dirname(parent.frame(2)$ofile)

返回:dirname(parent.frame(2)$ofile)中的错误:预期一个字符向量参数 ...因为parent.frame为空
frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])

返回:空,因为frame_files是一个长度为0的列表。
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))
    }
}

返回:path.expand(path)中的错误:无效的“path”参数

我也看到了这里这里这里这里的所有答案。没有成功。

使用RStudio 1.1.383工作

编辑:如果不需要外部库来实现这一点,那就太好了。


可能 source("<路径>/script.R", chdir = TRUE) 会起作用,因为帮助文件关于 chdir 的说明如下:如果为 TRUE 并且文件是一个路径名,则 R 工作目录会在评估过程中暂时更改为包含文件的目录。 您收到的错误消息表明 R 在当前工作目录中找不到名为 script.R 的文件。 - lmo
我的文件名是lpp.R,我确实给了这个名字...我记得在其他版本的RStudio中,我可以轻松地使用父框架完成它,现在什么也没有。 - Panos Kalatzantonakis
在R Studio项目文件夹中,假设所有文件路径都相对于项目的根目录是一个好习惯。请参见停止工作目录的疯狂。我个人不使用它,但推荐使用here包来查找给定文件的位置。 - Paul Rougieux
@Imo -- 这只在你源文件的情况下有效,但如果你使用 Rscript 调用它则不行。 - abalter
1
@PaulRougieux -- 这仅适用于您正在运行(或引用)的文件位于项目目录中的情况。如果您正在运行(或引用)另一个位置的文件,则不起作用。 - abalter
10个回答

123

在RStudio中,您可以使用以下命令获取当前在源代码窗格中显示的文件路径:

rstudioapi::getSourceEditorContext()$path

如果您只需要目录,请使用

dirname(rstudioapi::getSourceEditorContext()$path)

如果您想要知道使用source(filename)运行的文件的名称,那就有点难了。您需要在堆栈中某个位置查找变量srcfile。向后查找的步骤取决于编写的方式,但是大约需要向后查找4步:例如,

fi <- tempfile()
writeLines("f()", fi)
f <- function() print(sys.frame(-4)$srcfile)
source(fi)
fi

最后两行应该打印出相同的内容。


这个很棒。好处是与“here”包相比,它有更少的依赖性。事实是,我希望能够不使用库来完成这个。我会等待一两天看看是否有人有什么要补充,然后我就会接受它。谢谢。 - Panos Kalatzantonakis
我刚刚在办公室的另一台电脑上进行了干净的安装。最新的R Studio(1.1.383),最新的R版本(R version 3.4.2 (2017-09-28))。现在我遇到了这个错误: Error: 'getSourceEditorContext' is not an exported object from 'namespace:rstudioapi' - Panos Kalatzantonakis
@PanosKal.,你可能需要重新考虑为什么需要获取当前脚本的完整路径。Jennybc在Stop the working directory insanity中指出,脚本中加载的所有本地数据都应该使用相对于项目主目录的路径。然后,您编写的任何脚本都假定工作目录始终设置为项目的基本路径。默认情况下,R Studio在加载项目时将始终setwd()到项目主目录。 - Paul Rougieux
1
我需要让代码具备可移植性,将所有数据放在特定文件夹中,并使其在其他人的电脑上尽可能快地运行。特别是那些只知道按播放按钮并查看代码生成结果而无需任何用户干预的人。 - Panos Kalatzantonakis
2
这个不像 Rscript test.R 那样在终端运行。 - Onur Demir
显示剩余4条评论

25

2019年3月更新

根据Alexis Lucattini和user2554330的回答,为了使其在命令行和RStudio中都能运行,并解决“as_tibble”弃用消息。

library(tidyverse)
getCurrentFileLocation <-  function()
{
    this_file <- commandArgs() %>% 
    tibble::enframe(name = NULL) %>%
    tidyr::separate(col=value, into=c("key", "value"), sep="=", fill='right') %>%
    dplyr::filter(key == "--file") %>%
    dplyr::pull(value)
    if (length(this_file)==0)
    {
      this_file <- rstudioapi::getSourceEditorContext()$path
    }
    return(dirname(this_file))
}

2
这实际上是有效的,并且应该成为所有相关问题中被接受的答案。 - abalter
1
不错。在 if (length ...) { } 中,在 this_file <- ... 下面,我添加了 if(this_file=="") message("您正在新的 R 脚本窗口中在 RStudio 中运行代码,因此文件名为空。") - user3799203
好的回答!它还非常详细地介绍了当前使用tidyverse管道而不是循环进行编程R的方式。 - Hilton Fernandes

20
TLDR: here包(可在CRAN上获取)可以帮助您从项目的根目录构建路径。使用here()配置的R项目可以与使用不同笔记本电脑或服务器的同事共享,并且相对于项目的根目录构建的路径仍将起作用。开发版本位于github.com/r-lib/here

使用git

您肯定会将R代码存储在某个目录中。该目录可能是git存储库和/或R Studio项目的一部分。我建议所有路径都相对于该项目的根目录进行构建。例如,假设您有一个R脚本,创建可重用的绘图函数,并且您有一个R markdown笔记本,在其中加载该脚本并在漂亮(非常漂亮)的文档中绘制图形。项目树将类似于此:

├── notebooks
│   ├── analysis.Rmd
├── R
│   ├── prepare_data.R
│   ├── prepare_figures.R

你可以从 analysis.Rmd 笔记本中使用 here() 导入绘图函数,如下所示:

source(file.path(here::here("R"), "prepare_figures.R"))

为什么不能使用setwd()?

Hadley Wickham在Stackoverflow评论中指出

"您不应该在R代码中使用setwd() - 它基本上破坏了使用工作目录的想法,因为您不能再轻松地在计算机之间移动代码。– hadley Nov 20 '10 at 23:44 "

Ode to the here package中:

您是否:在脚本中使用setwd()?请停止这样做。这会使您的脚本非常脆弱,只能在一个时间和地点运行。一旦您重命名或移动目录,它就会失效。或者你有了新电脑?或者其他人需要运行你的代码?[...] 经典问题表述:在具有子目录的项目中构建路径和/或设置工作目录时遇到麻烦。特别是如果您使用R Markdown和knitr,由于其默认行为“工作目录=此文件所在的目录”,它会使很多人产生困扰。[...]

安装here包:

install.packages("here")
library(here)
here()
here("construct","a","path")

here() 函数的文档:

在包加载时,从当前工作目录开始向上遍历目录层次结构,直到找到满足以下至少一个条件的目录:

  • 包含一个与 [.]Rproj$ 匹配且第一行内容匹配 ^Version: 的文件
  • [... 其他选项 ...]
  • 包含一个名为 .git 的目录

一旦确定了根目录,在活动 R 会话期间根目录不会更改。然后,here() 将参数追加到根目录。

你可以在 github 上获取 here 包的开发版本

那么

项目目录外的文件怎么办?

如果要加载或源代码目录之外的文件,推荐的方式是在操作系统级别使用环境变量。不同笔记本电脑或服务器上运行您的 R 代码的其他用户需要设置相同的环境变量。优点是它是可移植的。

data_path <- Sys.getenv("PROJECT_DATA")
df <- read.csv(file.path(data_path, "file_name.csv"))

注意:有一长串环境变量可以影响R会话。

那么如果有许多项目彼此引用呢?

现在是时候创建一个R包了。


5
只有在你在同一项目树中 sourceRscript 文件时,这才有效。如果你调用的是其他地方存在的实用脚本,就不会有效。 - abalter
2
假设 R 不适合进行实际可靠的开发。如果你有多个相互依赖的包,那么相对于给定文件路径来导入是难以置信的困难。你唯一的选择就是全局从根脚本导入所有的内容。这对于可靠的开发是不可接受的。 - Stefano Borini
3
@PaulRougieux,这不是关于加载数据的问题,而是关于导入其他 R 文件的问题。例如,需要使用 source()。与 Python 的 import 相比,情况非常糟糕,因为你必须使用非常差劲的技巧来确保正确可靠的导入。当你必须编写生产就绪的代码并具有一定程度的控制时,这非常重要。 - Stefano Borini
1
@PaulRougieux 这也不是包的问题。您不能在同一代码库中嵌套层次结构或导入文件A导入文件B(例如,在不同文件中的代码片段之间,这些文件是同一软件包的一部分)。 - Stefano Borini
2
这不是 R 包的工作方式,你需要一个包名称空间,并在该名称空间内相互调用函数。 - Paul Rougieux
显示剩余5条评论

8
如果你通过命令行等方式运行R脚本
Rscript /path/to/script.R

下面的函数将把this_file赋值为/path/to/script
library(tidyverse)
get_this_file <- function() {
  commandArgs() %>%
    tibble::enframe(name = NULL) %>%
    tidyr::separate(
      col = value, into = c("key", "value"), sep = "=", fill = "right"
    ) %>%
    dplyr::filter(key == "--file") %>%
    dplyr::pull(value)
}
this_file <- get_this_file()
print(this_file)

它对我不起作用,返回 character(0) - understorey

7

以下是一个用于获取R、RStudio或Rscript文件路径的自定义函数:

stub <- function() {}
thisPath <- function() {
  cmdArgs <- commandArgs(trailingOnly = FALSE)
  if (length(grep("^-f$", cmdArgs)) > 0) {
    # R console option
    normalizePath(dirname(cmdArgs[grep("^-f", cmdArgs) + 1]))[1]
  } else if (length(grep("^--file=", cmdArgs)) > 0) {
    # Rscript/R console option
    scriptPath <- normalizePath(dirname(sub("^--file=", "", cmdArgs[grep("^--file=", cmdArgs)])))[1]
  } else if (Sys.getenv("RSTUDIO") == "1") {
    # RStudio
    dirname(rstudioapi::getSourceEditorContext()$path)
  } else if (is.null(attr(stub, "srcref")) == FALSE) {
    # 'source'd via R console
    dirname(normalizePath(attr(attr(stub, "srcref"), "srcfile")$filename))
  } else {
    stop("Cannot find file path")
  }
}

https://gist.github.com/jasonsychau/ff6bc78a33bf3fd1c6bd4fa78bbf42e7


4
另一种获取当前脚本路径的选项是 funr::get_script_path(),你不需要使用 RStudio 运行你的脚本。

funr是什么? - abalter
这是一个包,你可以在这里找到更多相关信息。在R中,即使你没有将一个包附加到当前工作环境中,你也可以通过以下方式使用它们的函数:package::function() - Manuel Sánchez Mendoza
3
假设你有一个位于/path/to/project/script.R的脚本,在脚本内部有一个语句funr::get_script_path(),它将返回值/path/to/project。注意:它返回的是当前文件的完整目录路径,而不是应该是/path/to/project/script.R完整路径。尽管如此,该函数在我的情况下有效。 - Abel Callejo
在RStudio中对我返回NULL。 - Matthias Munz

1
以下代码可获取运行R脚本的目录,无论是在Rstudio中运行还是使用Rscript命令从命令行运行:
if (rstudioapi::isAvailable()) {
  if (require('rstudioapi') != TRUE) {
    install.packages('rstudioapi')
  }else{
    library(rstudioapi) # load it
  }
 wdir <- dirname(getActiveDocumentContext()$path)
}else{
 wdir <- getwd()
}

setwd(wdir)

1
我在这些方面都遇到了麻烦,因为它们依赖于我在设置工作目录之前无法使用的库(因为packrat)。这就是为什么我需要先获取路径的原因。

所以,这里有一种只使用基本R的方法。(编辑后还可以处理路径中的windows \字符)

args = commandArgs()

scriptName = args[substr(args,1,7) == '--file=']

if (length(scriptName) == 0) {
  scriptName <- rstudioapi::getSourceEditorContext()$path
} else {
  scriptName <- substr(scriptName, 8, nchar(scriptName))
}

pathName = substr(
  scriptName, 
  1, 
  nchar(scriptName) - nchar(strsplit(scriptName, '.*[/|\\]')[[1]][2])
)


1
以下解决了三种情况下的问题:RStudio源代码按钮、RStudio R控制台(如果文件仍在“源”窗格中,则为source(...))或通过Rscript使用操作系统控制台:
this_file = gsub("--file=", "", commandArgs()[grepl("--file", commandArgs())])
if (length(this_file) > 0){
  wd <- paste(head(strsplit(this_file, '[/|\\]')[[1]], -1), collapse = .Platform$file.sep)
}else{
  wd <- dirname(rstudioapi::getSourceEditorContext()$path)
}

print(wd)

适用于RStudio的调整 - shosaco

0
如果您不想使用(或必须记住)代码,只需将鼠标悬停在脚本上即可显示路径。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接