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

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

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

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


当前回答

我自己刚算出来的。为了确保脚本的可移植性,总是以以下开头:

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

It works because "." translates like the Unix command $PWD. Assigning this string to a character object allows you to then insert that character object into setwd() and Presto your code will always run with its current directory as the working directory, no matter whose machine it is on or where in the file structure it is located. (Extra bonus: The wd object can be used with file.path() (ie. file.path(wd, "output_directory") to allow for the creation of a standard output directory regardless of the file path leading to your named directory. This does require you to make the new directory before referencing it this way but that, too, can be aided with the wd object.

或者,下面的代码执行完全相同的事情:

wd <- getwd()
setwd(wd)

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

setwd(".")

其他回答

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

我的一切都在一个!(—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="/"))
}

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

我在一个高性能计算集群环境中工作。我在不同的地方开发代码,而不是在生产运行的地方。在开发过程中,我通常从命令行交互式地调用R(而不是使用RStudio)。有很多来源(“foo.R”)正在进行。

在生产运行期间,我通常编写一个bash脚本,尝试不同的参数,并在单独的目录中运行每一组参数。bash脚本利用了工作负载管理器(即SLURM)。在这种环境中,设置环境变量很简单。考虑到这一点,下面的解决方案最适合我。

其他。R

my_message <- function(){
return("R is awkward")
}

foo。R

srcpath = Sys.getenv("R_SRC")
# Check if runnning w/o setting R_SRC - presumably done in directory of development, i.e. /path/to/R/code
if(srcpath == ""){
    srcpath="./"
}
source(sprintf("%s/other.R", srcpath))
string = my_message()
print(string)

如果从R交互式shell中运行,并且在/path/到/R/code中运行,那么简单

> source("foo.R")

如果不是从交互式shell运行,也不是从/path/to/R/code运行,首先设置环境变量R_SRC,然后调用Rscript

$ export R_SRC=/path/to/R/code/
$ Rscript /path/to/R/code/foo.R