我有一个名为的脚本,其中foo.R
包含另一个脚本other.R
,它位于同一目录中:
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
但我想R
找到other.R
不管当前工作目录是什么。
也就是说,foo.R
需要知道自己的路径。我怎样才能做到这一点?
您可以使用该commandArgs
函数获取由 Rscript 传递给实际 R 解释器的所有选项,并在它们中搜索--file=
. 如果您的脚本是从该路径启动的,或者它是使用完整路径script.name
启动的,则以下内容将以'/'
. 否则,它必须是相对的cwd
,您可以连接两个路径以获得完整路径。
编辑:听起来您只需要script.name
上述内容并剥离路径的最后一部分。我已经删除了不需要的cwd()
示例并清理了主脚本并发布了我的other.R
. 只需将此脚本和other.R
脚本保存到同一目录中,chmod +x
然后运行主脚本。
主.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"
这就是我相信德曼正在寻找的东西。
从 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))
}
}
frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])
不要问我它是如何工作的,因为我忘记了:/
这对我有用
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
来自获取 R 脚本路径的rakensi的答案是最正确和最出色的恕我直言。然而,它仍然是一个包含虚拟功能的 hack。我在这里引用它,以便其他人更容易找到它。
sourceDir <- getSrcDirectory(function(dummy) {dummy})
这给出了放置语句的文件的目录(定义了虚拟函数的位置)。然后可以使用它来设置工作目录并使用相对路径,例如
setwd(sourceDir)
source("other.R")
或创建绝对路径
source(paste(sourceDir, "/other.R", sep=""))
我的一体机!(--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)
}
}
}
}
}
Supressingfire 答案的精简变体:
source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}
这对我有用。只需将其从命令行参数中提取出来,去除不需要的文本,执行目录名,最后从中获取完整路径:
args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
截至 2020 年 11 月 11 日,我已经为此在 CRAN 和 GitHub 上提供了一个名为“this.path”的包。
安装它使用:
utils::install.packages("this.path")
# 版本 0.5.0,稳定
remotes::install_github("ArcadeAntics/this.path")
# 版本 0.5.1,实验性
然后通过以下方式使用它:
this.path::this.path()
或者
library(this.path)
this.path()
下面的答案是我的原始答案,仅供参考,尽管它的功能比上面可用的最新版本少了很多。改进包括:
base::source
使用with 参数时正确规范化路径chdir = TRUE
base::source
使用(即“file://absolute or relative path”和“file:///absolute path”)处理文件 URLbase::source
this.path
与 中的 URL 兼容source
,即:source("https://host/path/to/file")
如果this.path
在文件中使用,它将返回"https://host/path/to/file"
. "http://"
这也适用于以、"ftp://"
和开头的 URL "ftps://"
。例如,尝试:
source("https://raw.githubusercontent.com/ArcadeAntics/this.path/main/tests/this.path_w_URLs.R")
testthat::source_file
here
,类似于here::here
,用于指定相对于执行脚本目录的绝对文件路径list.dirs(system.file(package = "translations"), full.names = FALSE, recursive = FALSE)
this.path
,从而可以更快地在同一脚本中使用后续时间并且独立于工作目录。这意味着在使用相对路径或从 shell 运行 R 时setwd
将不再中断(只要在该脚本中第一次调用之后使用)this.path
base::source
setwd
this.path
原答案:
我的回答是对 Jerry T 的回答的改进。source
我发现的问题是他们通过检查是否ofile
在堆栈的第一帧中找到变量来猜测是否进行了调用。这不适用于嵌套的源调用,也不适用于从非全局环境进行的源调用。此外,顺序错误。我们必须在检查 shell 参数之前查找源调用。这是我的解决方案:
this.path <- function (verbose = getOption("verbose"))
{
where <- function(x) if (verbose)
cat("Source: ", x, "\n", sep = "")
# loop through functions that lead here from most recent to earliest looking
# for an appropriate source call (a call to function base::source or base::sys.source)
# an appropriate source call is a source call in which
# argument 'file' has been evaluated (forced)
# this means, for example, the following is an inappropriate source call:
# source(this.path())
# the argument 'file' is stored as a promise
# containing the expression "this.path()"
# when the value of 'file' is requested, it assigns the value
# returned by evaluating "this.path()" to variable 'file'
# there are two functions on the calling stack at
# this point being 'source' and 'this.path'
# clearly, you don't want to request the 'file' argument from that source
# call because the value of 'file' is under evaluation right now!
# the trick is to ask if variable ('ofile' for base::source, 'exprs' for base::sys.source)
# exists in that function's evaluation environment. this is because that
# variable is created AFTER argument 'file' has been forced
# if that variable does exist, then argument 'file' has been forced and the
# source call is deemed appropriate. For base::source, the filename we want
# is the variable 'ofile' from that function's evaluation environment. For
# base::sys.source, the filename we want is the variable 'file' from that
# function's evaluation environment.
# if that variable does NOT exist, then argument 'file' hasn't been forced and
# the source call is deemed inappropriate. The 'for' loop moves to the next
# function up the calling stack (if available)
#
# unfortunately, there is no way to check the argument 'fileName' has been forced
# for 'debugSource' since all the work is done internally in C. Instead,
# we have to use a 'tryCatch' statement. When we ask for an object by name
# using 'get', R is capable of realizing if a variable is asking for its
# own definition (a recursive definition). The exact error is "promise already
# under evaluation" which indicates that the promise evaluation is requesting
# its own value. So we use the 'tryCatch' to get the argument 'fileName'
# from the evaluation environment of 'debugSource', and if it does not raise
# an error, then we are safe to return that value. If not, the condition
# returns false and the 'for' loop moves to the next function up the calling
# stack (if available)
dbs <- if (.Platform$GUI == "RStudio")
get("debugSource", "tools:rstudio", inherits = FALSE)
for (n in seq.int(sys.nframe(), 1L)[-1L]) {
if (identical(sys.function(n), base::source) &&
exists("ofile", envir = sys.frame(n), inherits = FALSE)) {
path <- get("ofile", envir = sys.frame(n), inherits = FALSE)
if (!is.character(path))
path <- summary.connection(path)$description
where("call to function source")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), base::sys.source) &&
exists("exprs", envir = sys.frame(n), inherits = FALSE)) {
path <- get("file", envir = sys.frame(n), inherits = FALSE)
where("call to function sys.source")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), dbs) &&
tryCatch({
path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
TRUE
}, error = function(c) FALSE)) {
where("call to function debugSource in RStudio")
return(normalizePath(path, mustWork = TRUE))
}
}
# if the for loop is passed, no appropriate
# source call was found up the calling stack
# next, check if the user is running R from a shell
# on a Windows OS, the GUI is "RTerm"
# on a Unix OS, the GUI is "X11"
if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" || # running R from a shell on Windows
.Platform$OS.type == "unix" && .Platform$GUI == "X11") { # running R from a shell under Unix-alikes
# get all shell arguments that start with "--file="
# check the number of shell arguments starting with "--file="
# in case more or less than one were supplied
path <- grep("^--file=", commandArgs(), value = TRUE)
if (length(path) == 1L) {
path <- sub("^--file=", "", path)
where("shell argument 'FILE'")
return(normalizePath(path, mustWork = TRUE))
}
else if (length(path)) {
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from a shell where formal argument 'FILE' matched by multiple actual arguments")
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from a shell where argument 'FILE' is missing")
}
else if (.Platform$GUI == "RStudio") { # running R from 'RStudio'
# function ".rs.api.getActiveDocumentContext" from the environment "tools:rstudio"
# returns a list of information about the document where your cursor is located
#
# function ".rs.api.getSourceEditorContext" from the environment "tools:rstudio"
# returns a list of information about the document open in the current tab
#
# element 'id' is a character string, an identification for the document
# element 'path' is a character string, the path of the document
adc <- get(".rs.api.getActiveDocumentContext",
mode = "function", "tools:rstudio", inherits = FALSE)()
if (adc$id != "#console") {
path <- adc$path
if (nzchar(path)) {
where("active document in RStudio")
return(normalizePath(path, mustWork = TRUE))
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* active document in RStudio does not exist")
}
sec <- get(".rs.api.getSourceEditorContext", mode = "function",
"tools:rstudio", inherits = FALSE)()
if (!is.null(sec)) {
path <- sec$path
if (nzchar(path)) {
where("source document in RStudio")
return(normalizePath(path, mustWork = TRUE))
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* source document in RStudio does not exist")
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from RStudio with no documents open")
}
else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") { # running R from 'RGui' on Windows
# on a Windows OS only, the function "getWindowsHandles" from the base
# package "utils" returns a list of external pointers containing the windows
# handles. The thing of interest are the names of this list, these should
# be the names of the windows belonging to the current R process. Since
# RGui can have files besides R scripts open (such as images), a regular
# expression is used to subset only windows handles with names that exactly
# match the string "R Console" or end with " - R Editor". I highly suggest
# that you NEVER end a document's filename with " - R Editor". From there,
# similar checks are done as in the above section for 'RStudio'
wh <- names(utils::getWindowsHandles(pattern = "^R Console$| - R Editor$",
minimized = TRUE))
if (!length(wh))
stop("no windows in RGui; should never happen, please report!")
path <- wh[1L]
if (path != "R Console") {
path <- sub(" - R Editor$", "", path)
if (path != "Untitled") {
where("active document in RGui")
return(normalizePath(path, mustWork = TRUE))
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* active document in RGui does not exist")
}
path <- wh[2L]
if (!is.na(path)) {
path <- sub(" - R Editor$", "", path)
if (path != "Untitled") {
where("source document in RGui")
return(normalizePath(path, mustWork = TRUE))
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* source document in RGui does not exist")
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from RGui with no documents open")
}
else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") { # running R from 'RGui' on Unix
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run from AQUA which requires a source call on the calling stack")
}
else stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
"* R is being run in an unrecognized manner")
}
我已经总结并将这个问题的答案扩展到rprojrootthisfile()
中的一个新函数中。也适用于针织。knitr
我尝试了这个问题的几乎所有内容,Getting path of an R script,Get the path of a current script,Find location of current .R file and R command for setting working directory to source file location in Rstudio,但最后自己手动找到了浏览 CRAN 表,发现
scriptName
图书馆
它提供了current_filename()
函数,当在 RStudio 中进行采购以及通过 R 或 RScript 可执行文件调用时,它会返回脚本的正确完整路径。
I liked steamer25's solution as it seems the most robust for my purposes. However, when debugging in RStudio (in windows), the path would not get set properly. The reason being that if a breakpoint is set in RStudio, sourcing the file uses an alternate "debug source" command which sets the script path a little differently. Here is the final version which I am currently using which accounts for this alternate behavior within RStudio when debugging:
# @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))
}
}
}
我也有这个问题,以上解决方案都不适合我。也许有source
或之类的东西,但还不够清楚。
我发现这个对我来说很优雅的解决方案:
paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")
重要的是它fileSnapshot()
可以为您提供有关文件的大量信息。它返回一个包含 8 个元素的列表。当您选择path
作为列表元素时,路径将\\
作为分隔符返回,因此其余代码只是为了更改它。
我希望这有帮助。
我只是自己解决了这个问题。为确保脚本的可移植性,请始终以:
wd <- setwd(".")
setwd(wd)
它之所以有效,是因为“。” 像 Unix 命令 $PWD 一样翻译。将此字符串分配给字符对象允许您将该字符对象插入到 setwd() 中,并且Presto您的代码将始终以其当前目录作为工作目录运行,无论它在哪台机器上或文件结构中的什么位置位于。(额外的好处:wd 对象可以与 file.path() 一起使用(即 file.path(wd, "output_directory") 以允许创建标准输出目录,而不管通向您的命名目录的文件路径如何。这确实需要您在以这种方式引用它之前创建新目录,但这也可以通过 wd 对象来帮助。
或者,以下代码执行完全相同的操作:
wd <- getwd()
setwd(wd)
或者,如果您不需要对象中的文件路径,您可以简单地:
setwd(".")
您可以将 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
我喜欢这种方法:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
如果不是脚本,而是foo.R
知道它的路径位置,如果您可以更改代码以始终引用source
来自公共的所有 'd 路径,root
那么这些可能会有很大帮助:
给定
/app/deeply/nested/foo.R
/app/other.R
这将起作用
#!/usr/bin/env Rscript
library(here)
source(here("other.R"))
有关如何定义项目根的信息,请参见https://rprojroot.r-lib.org/ 。
请注意,getopt 包提供了该get_Rscript_filename
功能,该功能仅使用此处提供的相同解决方案,但已在标准 R 模块中为您编写,因此您不必将“获取脚本路径”功能复制并粘贴到每个脚本中你写。
请参阅findSourceTraceback()
R.utils包,其中
在所有调用帧中查找由 source() 生成的所有 'srcfile' 对象。这使得找出当前由 source() 编写脚本的文件成为可能。
我在上面的实现中遇到了问题,因为我的脚本是从符号链接目录操作的,或者至少这就是我认为上述解决方案对我不起作用的原因。按照@ennukiller 的回答,我将我的 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)
99% 的案例你可能会简单地使用:
sys.calls()[[1]] [[2]]
它不适用于脚本不是第一个参数的疯狂调用,即source(some args, file="myscript")
. 在这些花哨的情况下使用@hadley。
通过查看调用堆栈,我们可以获得每个正在执行的脚本的文件路径,其中最有用的两个可能是当前正在执行的脚本,或者是第一个要获取的脚本(条目)。
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) ))()
我会使用@steamer25 方法的变体。关键是即使我的会话是通过 Rscript 启动的,我也更喜欢获取最后一个源脚本。以下代码片段,当包含在文件中时,将提供一个thisScript
包含脚本规范化路径的变量。我承认(ab)使用 source'ing,所以有时我调用 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))
}
})()
Steamer25 的方法有效,但前提是路径中没有空格。在 macOS 上,至少cmdArgs[match]
返回类似于/base/some~+~dir~+~with~+~whitespace/
for的东西/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 块。
#!/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)
失败(如果可能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')
令人惊讶的是,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")
我在 HPC 集群环境中工作。我在与生产运行不同的位置开发代码。在开发过程中,我通常从命令行以交互方式调用 R(不使用 RStudio)。有很多source("foo.R")
事情要做。
在生产运行期间,我通常会编写一个 bash 脚本来尝试不同的参数并在单独的目录中运行每组参数。bash 脚本利用工作负载管理器(即 SLURM)。在这种环境下,设置一个环境变量是微不足道的。考虑到这一点,以下解决方案最适合我。
其他
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 和 inside 运行它/path/to/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