我有一个名为foo的脚本。R包含另一个脚本other。R,在同一个目录下:

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

但我想让R找到另一个。R,不管当前工作目录是什么。

换句话说,就是foo。R需要知道自己的路径。我该怎么做呢?


当前回答

I would use a variant of @steamer25 's approach. The point is that I prefer to obtain the last sourced script even when my session was started through Rscript. The following snippet, when included on a file, will provided a variable thisScript containing the normalized path of the script. I confess the (ab)use of source'ing, so sometimes I invoke Rscript and the script provided in the --file argument sources another script that sources another one... Someday I will invest in making my messy code turns into a package.

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))
  }
})()

其他回答

如果而不是脚本,foo。R,知道它的路径位置,如果你可以改变你的代码,总是从一个公共根引用所有的源路径,那么这些可能会有很大的帮助:

https://github.com/r-lib/rprojroot或https://rprojroot.r-lib.org/ https://here.r-lib.org/

鉴于

/ app /深度/嵌套/ foo。R /应用程序/其他。R

这是可行的

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

参见https://rprojroot.r-lib.org/了解如何定义项目根。

只是在上面的答案的基础上,作为安全检查,您可以添加一个包装器,当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')

你可以在一个bash脚本中包装r脚本,并检索脚本的路径作为bash变量,如下所示:

#!/bin/bash
     # [environment variables can be set here]
     path_to_script=$(dirname $0)

     R --slave<<EOF
        source("$path_to_script/other.R")

     EOF

这个问题有一个简单的解决办法。这个命令:

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

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

参见R.utils包的findSourceTraceback(),其中

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