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

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

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

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


当前回答

99%的情况你可以简单地使用:

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

它将不适用于脚本不是第一个参数的疯狂调用,即source(some args, file="myscript")。在这些奇特的情况下使用@hadley's。

其他回答

当从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))
        }
}

你可以使用commandArgs函数获取Rscript传递给实际R解释器的所有选项,并搜索——file=。如果你的脚本是从路径启动的,或者它是以一个完整的路径启动的,下面的script.name将以'/'开头。否则,它必须是相对于cwd,你可以连接两个路径,以获得完整的路径。

编辑:听起来你只需要上面的script.name,并剥离路径的最后一个组件。我已经删除了不需要的cwd()样本,并清理了主脚本,并张贴了我的其他. r。只需要保存这个脚本和其他脚本。R脚本放到同一个目录下,chmod +x它们,然后运行主脚本。

主要。接待员:

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

其他。接待员:

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"

我相信这就是德曼在找的东西。

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

我的一切都在一个!(—2019年9月1日更新以处理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)
                }
            }
        }
    }
}

Supressingfire回答的简化版本:

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