确定执行脚本的路径


255

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

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

但无论当前工作目录是什么,我都想R找到它other.R

换句话说,foo.R需要知道自己的路径。我怎样才能做到这一点?


2
号:(我没有从解决办法看出,实际工作的任何解决方案除了刚刚通过目录或使用环境变量。
弗兰克

3
使脚本甚至R neofites都能完全移植并可执行,真是太神奇了!
EtienneLow-Décarie'12

4
似乎所有答案都要求您在某个点输入路径(至少要提供文件来源)!如果您可以向某人发送一个压缩文件夹,然后在该文件夹中运行任何R脚本文件,然后从中读取并保存到该文件夹​​,那就太好了。
EtienneLow-Décarie'12

10
这个单一的问题实际上可能成为我完全转向Python的原因
Giacomo

5
@giac_man,我觉得R是挤满了上百像这样的小问题,所有的加起来使得很难奏效英寸
迈克尔·巴顿

Answers:


102

这里有一个简单的解决方案。该命令:

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

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


4
它对我不起作用。我在Windows中运行R。任何想法?
Ehsan88

4
遇到相同的错误,保存了脚本并重新安装并在Windows上运行R 3.2.0 ...
RalfB,2015年

27
当您尝试dirname(sys.frame(1)$ofile)直接从Rstudio 执行时,会发生此错误。使用源(“other.R”)在执行脚本时,它的工作原理确定,并dirname(sys.frame(1)$ofile)在里面"other.R"
Murta

4
当使用rscript.exe作为脚本调用时,即不使用source()时,出现“堆栈上没有那么多帧”错误。所以我不得不改用下面的Suppressingfire解决方案
Mark Adamson

3
NULL使用发亮时,当我将其放置在server.R中时,我会凝结
Paul

75

您可以使用该commandArgs函数获取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)

其他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所寻找的。


downmod是什么?
Suppressingfire

2
我降级是因为您的技术无法source按照OP的要求运行-但也许我误解了他/她的要求。但我
不能取消下调

但实际上,它确实可以与源一起正常工作!只要source(other.name),它就可以正常工作。
Suppressingfire

3
对于路径串联,最好使用other.name <- file.path(script.basename, "other.R")
Jason

1
当我尝试commandArgs(trailingOnly = FALSE)在闪亮的应用程序中的server.R内部运行时,我得到了[1] "RStudio" "--interactive"。没有有关从其调用目录的信息。
Paul

57

从R控制台“来源”时,我无法使用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))
        }
}

6
我喜欢它,因为它既可以在R中使用,Rscript也可以source()在R中使用。我建议normalizePath()在两个版本上都这样做,以便在两种情况下都提供完整的路径。
2014年

1
这是唯一有效的方法。请注意,library(base)
要使

2
先生,请您投票,因为这是对我
有用

1
如果这可以帮助任何人,因为原来的岗位,这将意味着source(file.path(dirname(thisFile()), "other.R"))foo.R。这对我有用。
金·

一个问题。假设在RStudio中,我提供了哪个调用main.R源。它将获取而不是的路径。这里有什么提示吗?helper.RthisFile()main.Rhelper.R
Wassadamo '19

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

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


2
在什么情况下有效?我运行时print(sys.frames())变成NULL。
Suppressingfire

1
@Suppressingfire:sys.frames返回调用堆栈的环境,因此只有在从函数调用时才有意义。尝试例如foo <- function() {bar <- function() print(sys.frames()); bar()}; foo()。我无法弄清楚@hadley的代码,因为环境没有ofile成员。
Richie Cotton

1
您必须在其中提供文件源-即,如果我保存该代码然后运行source("~/code/test.r")PATH它将设置为~/desktop。如果仅在最高级别对其进行评估,则它将返回NULL。
hadley

4
这不能回答我的问题。我需要自动找到“ other.R”文件。x$ofile未定义,因此frame_files为空。
Frank

@hadley,非常有用的代码。当它们处于活跃开发中时,我能够概括添加到几乎所有脚本的“重新加载当前脚本”实用程序功能。RScript重新加载器
Sim

29

这对我有用

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

4
我猜这只能在RStudio内部使用。从终端尝试,我得到了Error: RStudio not running
Ista

更具体地说,如果从R Studio中的R脚本运行,它就可以工作。即使在RStudio控制台上不会给出正确的结果""在我的情况

26

rakensi的答案来自 R脚本的获取路径中获得是最正确,最出色的恕我直言。然而,它仍然是结合了虚拟功能的黑客。我在这里引用它,以便其他人更容易找到它。

sourceDir <-getSrcDirectory(function(dummy){dummy})

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

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

或创建绝对路径

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

1
对我来说,您的解决方案是最好的。特别是因为它可以应用于Shiny应用程序,而不能应用于链接。
jcarlos '16

1
这里的getSrcDirectory是utils :: getSrcDirectory
RubenLaguna

5
在Linux / Mac上,这可能很好用,但是在Windows下的交互式RStudio会话中,它对我来说不起作用。sourceDir是空白的。
Contango

1
@Contango在交互式终端上,没有路径!!!您需要文件的路径。
pommedeterresautee

1
我正在character(0)。有什么建议吗?
abalter

16

我合而为一!(--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() {
    # 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)
                }
            }
        }
    }
}

不适用于交互式R会话;我收到:```> source(“ csf.R”)> csf()错误:RStudio没有运行```
ManicMailman

这很棒。有人可以打包吗?
乔·弗拉克

13

Supressingfire的答案的缩小版本:

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

这没有递归起作用;我来源的文件正在寻找数据文件(但目录错误)。
Unfun Cat 2015年

11

这对我有用。只需将其从命令行参数中移出,去除不需要的文本,执行目录名,最后从中获取完整路径:

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

8

我已经总结了该问题的答案,并将其扩展到rprojroot中的新函数thisfile()中。也可以用编织。knitr


6

我喜欢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))
        }
    }
}

Rstudio中的源代码为我提供了ofile,但debugSource提供了fileName,因此您的解决方案运行良好,但在我的情况下,代码注释不太正确
Mark Adamson

6

我从这个问题尝试了几乎所有东西,获取R脚本的路径获取当前脚本的路径查找当前.R文件的位置以及用于在Rstudio中将工作目录设置为源文件位置的R命令,但最后发现自己是手动的浏览CRAN表并找到

scriptName 图书馆

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


1
Package ‘scriptName’ was removed from the CRAN repository.- 现在怎么办?:o
Bojan P.

3

我也遇到了这个问题,上面的解决方案都不适合我。也许带有source或之类的东西,但还不够清楚。

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

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

重要的是,fileSnapshot()它可以为您提供有关文件的大量信息。它返回8个元素的列表。当您选择path作为列表元素时,该路径将\\作为分隔符返回,因此其余代码只是用于更改它。

我希望这有帮助。


1
在Linux机器上,这对我不起作用。它没有返回文件路径,而是返回了我当前所在的目录。我创建了一个名为TEST.R的测试脚本,其中包含以下代码行:print(fileSnapshot()$ path)我将其保存在此文件夹中:/ opt / home / boops / Desktop / Testfolder / TEST.RI然后导航到我的桌面并尝试运行该文件:boops @ linuxserver:〜/ Desktop $ Rscript /opt/home/boops/Desktop/Testfolder/TEST.R [1 ]“ / opt / home / boops / Desktop”
Boops Boops

也没有为我工作。使用“ here”库时,返回与“ here()”相同的内容。它返回了我当前打开的R项目的路径,但不是文件本身正在执行。
Joe Flack)

2

您可以将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

3
这要求您具有脚本路径。它不允许您制作可在任何地方运行的真正可移植的R脚本。
EtienneLow-Décarie'12

@EtienneLow-Décarie它不需要脚本路径,它是从bash获取的。主要问题是,这不是获取路径的可靠方法。像这样的东西是可取的,例如在stackoverflow.com/questions/59895/…path_to_script =“ $(cd” $(目录名“ $ {BASH_SOURCE [0]}”)“ && pwd)”
John Haberstroh

2

我喜欢这种方法:

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

2

我自己解决了这个问题。为确保脚本的可移植性,始终以以下内容开头:

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

之所以有效,是因为“。” 像Unix命令$ PWD一样进行翻译。将此字符串分配给字符对象后,您便可以将该字符对象插入到setwd()中,并且Presto您的代码将始终以其当前目录作为工作目录运行,无论它在谁的机器上或在文件结构中的哪个位置位于。(额外的好处:wd对象可以与file.path()(即file.path(wd,“ output_directory”)一起使用,以允许创建标准输出目录,而不管指向您命名目录的文件路径如何。这确实需要您在以这种方式引用之前创建新目录,但是wd对象也可以提供帮助。

或者,以下代码执行的操作完全相同:

wd <- getwd()
setwd(wd)

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

setwd(".")

11
不。那找到进程的目录,而不是文件本身。
user1071847 '12

这在Windows中以交互模式在RStudio中为我工作。
康坦戈

2

请注意,getopt包提供了该get_Rscript_filename功能,该功能仅使用此处介绍的相同解决方案,但是已经在标准R模块中为您编写了该功能,因此您不必将“获取脚本路径”功能复制并粘贴到每个脚本中你写。


即使我创建了一个打印其输出的脚本,然后使用R -e "library(getopt); testscript.R"
bokov

1
顾名思义,该函数需要使用来运行脚本Rscript
瑞安·汤普森

啊,哎呀。谢谢。
bokov '18

1

看到findSourceTraceback()所述的R.utils包,它

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


1

我的脚本是通过符号链接目录操作的,因此上述实现方式存在问题,或者至少这就是为什么我认为上述解决方案不适用于我。按照@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)

1

我将使用@ steamer25的方法的变体。关键是,即使我的会话是通过Rscript启动的,我也希望获得最后一个源脚本。以下代码段包含在文件中时,将提供一个thisScript包含脚本规范化路径的变量。我承认对源代码使用的滥用,因此有时我调用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))
  }
})()

1

您可能只使用99%的情况:

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

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


不过,不是来自RStudio内部,除非是在采购时
nJGL

1

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块。


1

如果foo.R知道脚本的路径位置而不是脚本,如果可以更改代码以始终引用source同一路径中的所有路径,root则可能会有很大帮助:

给定

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

这会工作

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

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


对我来说,这里的软件包确实可以完成工作,并且似乎是一个简单的解决方案
罗恩(Ron)

0
#!/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='/'))

我仍然收到错误“ sys.frame(1)中的错误:堆栈上没有那么多帧”
Michael Barton

0

令人惊讶的是,R中没有'$ 0'类型结构!您可以通过对用R编写的bash脚本进行system()调用来实现:

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,

0

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

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) ))()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.