Related
I have a script called foo.R that includes another script other.R, which is in the same directory:
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
But I want R to find that other.R no matter what the current working directory.
In other words, foo.R needs to know its own path. How can I do that?
Here there is a simple solution for the problem. This command:
script.dir <- dirname(sys.frame(1)$ofile)
returns the path of the current script file. It works after the script was saved.
You can use the commandArgs function to get all the options that were passed by Rscript to the actual R interpreter and search them for --file=. If your script was launched from the path or if it was launched with a full path, the script.name below will start with a '/'. Otherwise, it must be relative to the cwd and you can concat the two paths to get the full path.
Edit: it sounds like you'd only need the script.name above and to strip off the final component of the path. I've removed the unneeded cwd() sample and cleaned up the main script and posted my other.R. Just save off this script and the other.R script into the same directory, chmod +x them, and run the main script.
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)
other.R:
print("hello")
output:
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"
This is what I believe dehmann is looking for.
I couldn't get Suppressingfire's solution to work when 'source'ing from the R console.
I couldn't get hadley's solution to work when using Rscript.
Best of both worlds?
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)]])
Don't ask me how it works though, because I've forgotten :/
This works for me
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
The answer of rakensi from Getting path of an R script is the most correct and really brilliant IMHO. Yet, it's still a hack incorporating a dummy function. I'm quoting it here, in order to have it easier found by others.
sourceDir <- getSrcDirectory(function(dummy) {dummy})
This gives the directory of the file where the statement was placed (where the dummy function is defined). It can then be used to set the working direcory and use relative paths e.g.
setwd(sourceDir)
source("other.R")
or to create absolute paths
source(paste(sourceDir, "/other.R", sep=""))
My all in one! (--01/09/2019 updated to deal with RStudio Console)
#' 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)
}
}
}
}
}
I've made a package for this, available on CRAN and GitHub, called "this.path". The current version is 1.2.0 published on 2023-01-16, you can find it here:
https://CRAN.R-project.org/package=this.path
https://github.com/ArcadeAntics/this.path
Install it from CRAN:
utils::install.packages("this.path")
or install the development version from GitHub:
utils::install.packages("this.path", repos = "https://raw.githubusercontent.com/ArcadeAntics/PACKAGES")
and then use it by:
this.path::this.path()
or
library(this.path)
this.path()
The answer below is my original answer, kept just for reference, though it is quite a bit less functional than the most recent versions available above. Improvements include:
this.path() now works within VSCode
handling filenames with spaces when running an R script from a shell under Unix-alikes
handling both uses of running an R script from a shell (-f file and --file=file)
correctly normalizes the path when using source with argument chdir = TRUE
handling of file URLs with source (that is, "file://absolute or relative path" and "file:///absolute path")
better handling of a connection instead of a character string within source
this.path is compatible with URLs in source, that is:
source("https://host/path/to/file")
if this.path was used within the file, it would return "https://host/path/to/file". This also works for a URL beginning with "http://", "ftp://", and "ftps://". As an example, try:
source("https://raw.githubusercontent.com/ArcadeAntics/this.path/main/tests/this.path_w_URLs.R")
compatibility with package testthat and knitr, particularly testthat::source_file and knitr::knit
introduces function here, similar to here::here, for specifying an absolute file path, relative to the executing script's directory
on Windows, in Rgui, added support for all languagesĀ listed by list.dirs(system.file(package = "translations"), full.names = FALSE, recursive = FALSE)
saving the normalized path within its appropriate environment the first time this.path is called within a script, making it faster to use subsequent times within the same script and being independent of working directory. This means that setwd will no longer break this.path when using relative paths within source or when running R from a shell (as long as setwd is used AFTER the first call to this.path within that script)
Original Answer:
My answer is an improvement upon Jerry T's answer. The issue I found is that they are guessing whether a source call was made by checking if variable ofile is found in the first frame on the stack. This will not work with nested source calls, nor source calls made from a non-global environment. Additionally, the order is wrong. We must look for source call BEFORE checking the shell arguments. Here is my solution:
this.path <- function (verbose = getOption("verbose"))
{
# loop through functions that lead here from most recent to
# earliest looking for an appropriate source call (a call to
# function source / / sys.source / / debugSource in RStudio)
#
# an appropriate source call is one in which the file argument has
# been evaluated (forced)
#
# for example, this means `source(this.path())` is an inappropriate
# source call. the argument 'file' is stored as a promise
# containing the expression "this.path()". when the value of 'file'
# is requested, the expression is evaluated at which time there
# should be two functions on the calling stack 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 'file' has
# already been evaluated, the easiest way of which is to ask if a
# variable exists, one which is only created after the expression
# is necessarily evaluated.
#
# if that variable does exist, then argument 'file' has been forced
# and the source call is deemed appropriate. For 'source', the
# filename we want is the variable 'ofile' from that function's
# evaluation environment. For '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
#
# 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 promise). 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
debugSource <- if (.Platform$GUI == "RStudio")
get("debugSource", "tools:rstudio", inherits = FALSE)
for (n in seq.int(to = 1L, by = -1L, length.out = sys.nframe() - 1L)) {
if (identical(sys.function(n), 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
if (verbose)
cat("Source: call to function source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), sys.source) &&
exists("exprs", envir = sys.frame(n), inherits = FALSE))
{
path <- get("file", envir = sys.frame(n), inherits = FALSE)
if (verbose)
cat("Source: call to function sys.source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), debugSource) &&
tryCatch({
path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
TRUE
}, error = function(c) FALSE))
{
if (verbose)
cat("Source: call to function debugSource in RStudio\n")
return(normalizePath(path, mustWork = TRUE))
}
}
# no appropriate source call was found up the calling stack
# if (running R from RStudio)
if (.Platform$GUI == "RStudio") {
# ".rs.api.getActiveDocumentContext" from "tools:rstudio"
# returns a list of information about the document where your
# cursor is located
#
# ".rs.api.getSourceEditorContext" from "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
context <- get(".rs.api.getActiveDocumentContext",
"tools:rstudio", inherits = FALSE)()
active <- context[["id"]] != "#console"
if (!active) {
context <- get(".rs.api.getSourceEditorContext",
"tools:rstudio", inherits = FALSE)()
if (is.null(context))
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\n",
" (or source document has no path)")
}
path <- context[["path"]]
Encoding(path) <- "UTF-8"
if (nzchar(path)) {
if (verbose)
cat(if (active)
"Source: active document in RStudio\n"
else "Source: source document in RStudio\n")
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",
if (active)
"* active document in RStudio does not exist"
else "* source document in RStudio does not exist")
}
# if (running R from RStudio before .Platform$GUI is changed)
# this includes code evaluated in the site-wide startup profile file,
# user profile, and function .First (see ?Startup)
else if (isTRUE(Sys.getpid() == as.integer(Sys.getenv("RSTUDIO_SESSION_PID"))) {
stop("RStudio has not finished loading")
}
# if (running R from a shell)
else if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" || # on Windows
.Platform$OS.type == "unix" && .Platform$GUI == "X11") # under Unix-alikes
{
argv <- commandArgs()
# remove all trailing arguments
m <- match("--args", argv, 0L)
if (m)
argv <- argv[seq_len(m)]
argv <- argv[-1L]
# get all arguments starting with "--file="
FILE <- argv[startsWith(argv, "--file=")]
# remove "--file=" from the start of each string
FILE <- substring(FILE, 8L)
# remove strings "-"
FILE <- FILE[FILE != "-"]
n <- length(FILE)
if (n) {
FILE <- FILE[[n]]
if (verbose)
cat("Source: shell argument 'FILE'\n")
return(normalizePath(FILE, mustWork = TRUE))
} 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")
}
}
# if (running R from RGui on Windows)
else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {
# "getWindowsHandles" from "utils" (Windows exclusive) returns
# a list of external pointers containing the windows handles.
# The thing of interest are the names of this list, these are
# 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 start with "R Console" or end with
# " - R Editor". From there, similar checks are done as in the
# above section for 'RStudio'
x <- names(utils::getWindowsHandles(pattern = "^R Console| - R Editor$",
minimized = TRUE))
if (!length(x))
stop("no windows in Rgui; should never happen, please report!")
active <- !startsWith(x[[1L]], "R Console")
if (active)
x <- x[[1L]]
else if (length(x) >= 2L)
x <- x[[2L]]
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")
if (x == "Untitled - R Editor")
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
if (active)
"* active document in Rgui does not exist"
else "* source document in Rgui does not exist")
path <- sub(" - R Editor$", "", x)
if (verbose)
cat(if (active)
"Source: active document in Rgui\n"
else "Source: source document in Rgui\n")
return(normalizePath(path, mustWork = TRUE))
}
# if (running R from RGui on macOS)
else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {
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 is currently unimplemented\n",
" consider using RStudio until such a time when this is implemented")
}
# otherwise
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")
}
A slimmed down variant of Supressingfire's answer:
source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}
This works for me. Just greps it out of the command line arguments, strips off the unwanted text, does a dirname and finally gets the full path from that:
args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
I have wrapped up and extended the answers to this question into a new function thisfile() in rprojroot. Also works for knitting with knitr.
I tried almost everything from this question, Getting path of an R script, Get the path of current script, Find location of current .R file and R command for setting working directory to source file location in Rstudio, but at the end found myself manually browsing the CRAN table and found
scriptName library
which provides current_filename() function, which returns proper full path of the script when sourcing in RStudio and also when invoking via R or RScript executable.
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))
}
}
}
I just worked this out myself. To ensure portability of your script always begin it with:
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.
Alternately, the following code performs the exact same thing:
wd <- getwd()
setwd(wd)
or, if you don't need the file path in an object you can simply:
setwd(".")
I also had this problem, and none of the above solutions worked for me. Maybe with the source or things like that, but it was not clear enough.
I found this, for me elegant, solution:
paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")
The important thing in that is the fileSnapshot() that gives you a lot of information about a file. It returns a list of 8 elements. When you pick path as the list element, the path is returned with \\ as separator, so the rest of the code is just to change that.
I hope this helps.
You can wrap the r script in a bash script and retrieve the script's path as a bash variable like so:
#!/bin/bash
# [environment variables can be set here]
path_to_script=$(dirname $0)
R --slave<<EOF
source("$path_to_script/other.R")
EOF
I like this approach:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
Note that the getopt package provides the get_Rscript_filename function, which just uses the same solution presented here, but is already written for you in a standard R module, so you don't have to copy and paste the "get script path" function into every script you write.
If rather than the script, foo.R, knowing its path location, if you can change your code to always reference all source'd paths from a common root then these may be a great help:
https://github.com/r-lib/rprojroot or https://rprojroot.r-lib.org/
https://here.r-lib.org/
Given
/app/deeply/nested/foo.R
/app/other.R
This will work
#!/usr/bin/env Rscript
library(here)
source(here("other.R"))
See https://rprojroot.r-lib.org/ for how to define project roots.
I had issues with the implementations above as my script is operated from a symlinked directory, or at least that's why I think the above solutions didn't work for me. Along the lines of #ennuikiller's answer, I wrapped my Rscript in bash. I set the path variable using pwd -P, which resolves symlinked directory structures. Then pass the path into the 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)
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))
}
})()
99% of the cases you might simply use:
sys.calls()[[1]] [[2]]
It will not work for crazy calls where the script is not the first argument, i.e., source(some args, file="myscript"). Use #hadley's in these fancy cases.
Steamer25's approach works, but only if there is no whitespace in the path. On macOS at least the cmdArgs[match] returns something like /base/some~+~dir~+~with~+~whitespace/ for /base/some\ dir\ with\ whitespace/.
I worked around this by replacing the "~+~" with a simple whitespace before returning it.
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))
}
}
Obviously you can still extend the else block like aprstar did.
By looking at the call stack we can get the filepath of each script being executed, the two most useful will probably either be the currently executing script, or the first script to be sourced (entry).
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) ))()
The solution arrived in 2016. Many thanks to the author, Sahil Seth!
The package funr on CRAN and github provides the function sys.script() which gets the full path to the current script. It even references a similar SO post.
Thus, the solution is:
myscript.R:
#!/usr/bin/env Rscript
f <- funr::sys.script()
show(f)
and then executing the command:
user#somewhere:/home$ Rscript myscript.R
at the command line will output, e.g.:
"/home/path/to/myscript.R"
to the console.
See findSourceTraceback() of the R.utils package, which
Finds all 'srcfile' objects generated by source() in all call frames.
This makes it possible to find out which files are currently scripted by source().
#!/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='/'))
Amazing there is no '$0' type structure in R! You can do it with a system() call to a bash script written in R:
write.table(c("readlink -e $0"), file="scriptpath.sh",col=F, row=F, quote=F)
thisscript <- system("sh scriptpath.sh", intern = TRUE)
Then just split out the scriptpath.sh name for other.R
splitstr <- rev(strsplit(thisscript, "\\/")[[1]])
otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")
I work in an HPC cluster environment. I develop my code in a different location from where I do my production runs. During development, I'm usually calling R interactively from the command line (not using RStudio). There is lots of source("foo.R") going on.
During production runs, I usually write a bash script that tries different parameters and runs each set of parameters in a separate directory. The bash script utilizes the workload manager (i.e. SLURM). In this environment, it is trivial to set an environmental variable. With this in mind, the below solution works best for me.
other.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)
If running this from the R interactive shell and within /path/to/R/code, simply do
> source("foo.R")
If running not from the interactive shell and not running from /path/to/R/code, set the environmental variable R_SRC first, then call Rscript
$ export R_SRC=/path/to/R/code/
$ Rscript /path/to/R/code/foo.R
Just to build on the above answers, as a safety check, you could add a wrapper that asks the user to find the file if (for whatever reason) sys.frame(1) fails (as it might if interactive() == TRUE), or the sourced script is not where the main script expects it to be.
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')
I want to generate a text file containing the folowing lines:
http://example.com/file1.pdf
http://example.com/file2.pdf
http://example.com/file3.pdf
.
.
http://example.com/file1000.pdf
Can any one advise how to do it using unix command line, please?
Thank you
With an interating for loop
for (( i=1;i<=1000;i++ ));
do
echo "http://example.com/file$i.pdf";
done > newfile
With seq:
while read i;
do
echo "http://example.com/file$i.pdf";
done <<< $(seq 1000) > newfile
It is possible to create/run a python script file ato generate this. Using vim, nano, or any other terminal editor, create a python file as follows:
def genFile(fn, start, end):
with open(fn, "w+") as f:
f.writelines([f"http://example.com/file{str(i)}.pdf\n" for i in range(start, end+1)])
try:
fn = input("File Path: ") # can be relative
start = int(input("Start: ")) # inclusive
end = int(input("End: ")) # inclusive
genFile(fn, start, end)
except:
print("Invalid Input")
Once this is written to a file, let's call it script.py. We can run the following command to execute the script:
python script.py
Then, fill out the prompts for the file path, start, and end. This should result in all those lines printed in the file specified delimited by '\n'.
I am trying to run a R script from excel VBA. The script just queries data, formats and organizes it, and then exports it as a separate XLS file. However, I can not get the R script to run.
Going off of this post: (albeit the OG code is what I understood)
Running R from Excel VBA without RExcel
I don't have access to RExcel due to work restrictions on downloads. I have found my R.exe file paths and the macro below is able to run:
Dim rCommand As String
rCommand = "C:\Program Files\R\R-3.5.3\bin\R.exe --verbose U:\Reporting\Mix of Business Report\Morgan's Trials\Mix of Business Report v1.R"
'Timer Set to run full Model.R script
Application.Wait Now + TimeValue("00:01:05")
'Runs R Script and Arguments into process
shell rCommand, vbNormalFocus
'Timer Set to run full Model.R Script
Application.Wait Now + TimeValue("00:01:05")
I wanted my macro to run in the background, however the R window (NOT Rstudio, which is fine, as long as it works) doesn't run the R script.
Here is what I am shown in the "Rterm" window:
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported. Defaulting to Windows directory.
ARGUMENT 'U:\Reporting\Mix' __ ignored__
ARGUMENT 'of' __ ignored__
ARGUMENT 'Business' __ ignored__
ARGUMENT 'Report\Morgan's' __ ignored__
ARGUMENT 'Trials\Mix' __ ignored__
ARGUMENT 'of' __ ignored__
ARGUMENT 'Business' __ ignored__
ARGUMENT 'Report' __ ignored__
ARGUMENT 'v1.R' __ ignored__
'verbose' and 'quietly' are both true; being verbose then ..
now dyn.load("C:/Program Files/R/R-3.5.3/library/methods/libs/x64/methods.dll") ...
'verbose' and 'quietly' are both true; being verbose then ..
'verbose' and 'quietly' are both true; being verbose then ..
now dyn.load("C:/Program Files/R/R-3.5.3/library/utils/libs/x64/utils.dll") ...
'verbose' and 'quietly' are both true; being verbose then ..
Garbage collection 1 = 0+0+1 (level 2) ...
11.5 Mbytes of cons cells used (35%)
2.7 Mbytes of vectors used (4%)
now dyn.load("C:/Program Files/R/R-3.5.3/library/grDevices/libs/x64/grDevices.dll") ...
'verbose' and 'quietly' are both true; being verbose then ..
now dyn.load("C:/Program Files/R/R-3.5.3/library/graphics/libs/x64/graphics.dll") ...
'verbose' and 'quietly' are both true; being verbose then ..
now dyn.load("C:/Program Files/R/R-3.5.3/library/stats/libs/x64/stats.dll") ...
ending setup_Rmainloop(): R_Interactive = 1 {main.c}
>R_ReplConsole(): before "for(;;)" {main.c}
Why is my file path being ignored? I also tried changing the backslashes of my verbose r script path to forward slashes, same message.
Sorry if it's repetitive, I don't understand any of the other questions posted and the one linked above was my best shot.
It seems like the problem is in the path, because in UNC you need to put quotes in a path that contains spaces. Change the command like this and it will work:
Dim rCommand As String
rCommand = """C:\Program Files\R\R-3.5.3\bin\R.exe"" --verbose ""U:\Reporting\Mix of Business Report\Morgan's Trials\Mix of Business Report v1.R"""
Edit
Another problem was that R.exe shouldn't be used to simply execute scripts. We can use RScript.exe for that purpose and we can avoid the console closing, by adding && pause at the end of the command. Here's the complete code:
Dim rCommand As String
rCommand = """C:\Program Files\R\R-3.5.3\bin\RScript.exe"" --verbose ""U:\Reporting\Mix of Business Report\Morgan's Trials\Mix of Business Report v1.R"" && pause"
'Timer Set to run full Model.R script
Application.Wait Now + TimeValue("00:01:05")
'Runs R Script and Arguments into process
Shell rCommand, vbNormalFocus
'Timer Set to run full Model.R Script
Application.Wait Now + TimeValue("00:01:05")
Sync Method
A way to improve the function is to execute the shell with waitTillComplete flag, which executes the command with a Sync call. Here's the code:
Sub R_Exec()
Dim cmd As Object
Dim rCommand As String, rBin As String, rScript As String
Dim errorCode As Integer
Dim waitTillComplete As Boolean: waitTillComplete = True
Dim debugging As Boolean: debugging = True
Set cmd = VBA.CreateObject("WScript.Shell")
rBin = """C:\Program Files\R\R-3.5.3\bin\RScript.exe"""
rScript = """U:\Reporting\Mix of Business Report\Morgan's Trials\Mix of Business Report v1.R"""
'Check if the shell has to keep CMD window or not
If debugging Then
rCommand = "cmd.exe ""/k"" " & rBin & " " & rScript
Else
rCommand = rBin & " " & rScript
End If
Debug.Print rCommand 'Print the command for debug
'Runs R Script and Arguments into process, returning errorCode
errorCode = cmd.Run(rCommand, vbNormalFocus, waitTillComplete)
End Sub
With this Sub you can decide if keep the shell window open or not.
Hope this helps.
Alternatively, for cleaner code to avoid too many quote handling and extendable to many arguments consider building command line call from an array of arguments (borrowed from #UriGoren). Also, use CMD.exe /k to launch window of the automated Rscript (which ideally should be accessible through the PATH environment variable):
Sub Run_R_Script()
Dim args(0 To 3) As String
Dim rCommand As String
Dim i As Integer
args(0) = "CMD.exe"
args(1) = "/k"
'args(2) = "Rscript.exe" ' IF R BIN FOLDER IN PATH ENV VARIABLE
args(2) = "C:\Program Files\R\R-3.5.3\bin\RScript.exe"
args(3) = "U:\Reporting\Mix of Business Report\Morgan's Trials\Mix of Business Report v1.R"
rCommand = args(0)
For i = 1 To UBound(args)
rCommand = rCommand & " """ & args(i) & """"
Next i
Debug.Print rCommand ' CHECK COMMAND LINE CALL
'Timer Set to run full Model.R script
Application.Wait Now + TimeValue("00:01:05")
'Runs R Script and arguments into process
Shell rCommand, vbNormalFocus
'Timer Set to run full Model.R Script
Application.Wait Now + TimeValue("00:01:05")
End Sub
So I've followed this blog to create an executable file called r2jekyll
Unfortunately, I'm on Windows so I've had to create the .Rexec called r2jekyll differently
the code for r2jekyll is here:
#!/usr/bin/env Rscript
library(knitr)
# Get the filename given as an argument in the shell.
args = commandArgs(TRUE)
filename = args[1]
# Check that it's a .Rmd file.
if(!grepl(".Rmd", filename)) {
stop("You must specify a .Rmd file.")
}
# Knit and place in _posts.
dir = paste0("../_posts/", Sys.Date(), "-")
output = paste0(dir, sub('.Rmd', '.md', filename))
knit(filename, output)
# Copy .png files to the images directory.
fromdir = "{{ site.url }}/images"
todir = "../images"
pics = list.files(fromdir, ".png")
pics = sapply(pics, function(x) paste(fromdir, x, sep="/"))
file.copy(pics, todir)
unlink("{{ site.url }}", recursive = TRUE)
That all works fine, I can run my r2jekyll rexec (thanks to this blog) it runs but nothing happens
I'm up to the last step to run the r2jekyll on a file i've called first_test.Rmd
I run the following code in command prompt
cd (go to directory where my r2jekyll and my first_test.Rmd are sitting)
then
r2jekyll.rexec first_test.Rmd (the blog author used this code, he is on a mac)
and I get the following error
Error: You must specify a .Rmd file.
Execution halted
So my question is: how do I get my r2jekyll.Rexec to do its thing to the first_test.Rmd
I have a script called foo.R that includes another script other.R, which is in the same directory:
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
But I want R to find that other.R no matter what the current working directory.
In other words, foo.R needs to know its own path. How can I do that?
Here there is a simple solution for the problem. This command:
script.dir <- dirname(sys.frame(1)$ofile)
returns the path of the current script file. It works after the script was saved.
You can use the commandArgs function to get all the options that were passed by Rscript to the actual R interpreter and search them for --file=. If your script was launched from the path or if it was launched with a full path, the script.name below will start with a '/'. Otherwise, it must be relative to the cwd and you can concat the two paths to get the full path.
Edit: it sounds like you'd only need the script.name above and to strip off the final component of the path. I've removed the unneeded cwd() sample and cleaned up the main script and posted my other.R. Just save off this script and the other.R script into the same directory, chmod +x them, and run the main script.
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)
other.R:
print("hello")
output:
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"
This is what I believe dehmann is looking for.
I couldn't get Suppressingfire's solution to work when 'source'ing from the R console.
I couldn't get hadley's solution to work when using Rscript.
Best of both worlds?
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)]])
Don't ask me how it works though, because I've forgotten :/
This works for me
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
The answer of rakensi from Getting path of an R script is the most correct and really brilliant IMHO. Yet, it's still a hack incorporating a dummy function. I'm quoting it here, in order to have it easier found by others.
sourceDir <- getSrcDirectory(function(dummy) {dummy})
This gives the directory of the file where the statement was placed (where the dummy function is defined). It can then be used to set the working direcory and use relative paths e.g.
setwd(sourceDir)
source("other.R")
or to create absolute paths
source(paste(sourceDir, "/other.R", sep=""))
My all in one! (--01/09/2019 updated to deal with RStudio Console)
#' 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)
}
}
}
}
}
I've made a package for this, available on CRAN and GitHub, called "this.path". The current version is 1.2.0 published on 2023-01-16, you can find it here:
https://CRAN.R-project.org/package=this.path
https://github.com/ArcadeAntics/this.path
Install it from CRAN:
utils::install.packages("this.path")
or install the development version from GitHub:
utils::install.packages("this.path", repos = "https://raw.githubusercontent.com/ArcadeAntics/PACKAGES")
and then use it by:
this.path::this.path()
or
library(this.path)
this.path()
The answer below is my original answer, kept just for reference, though it is quite a bit less functional than the most recent versions available above. Improvements include:
this.path() now works within VSCode
handling filenames with spaces when running an R script from a shell under Unix-alikes
handling both uses of running an R script from a shell (-f file and --file=file)
correctly normalizes the path when using source with argument chdir = TRUE
handling of file URLs with source (that is, "file://absolute or relative path" and "file:///absolute path")
better handling of a connection instead of a character string within source
this.path is compatible with URLs in source, that is:
source("https://host/path/to/file")
if this.path was used within the file, it would return "https://host/path/to/file". This also works for a URL beginning with "http://", "ftp://", and "ftps://". As an example, try:
source("https://raw.githubusercontent.com/ArcadeAntics/this.path/main/tests/this.path_w_URLs.R")
compatibility with package testthat and knitr, particularly testthat::source_file and knitr::knit
introduces function here, similar to here::here, for specifying an absolute file path, relative to the executing script's directory
on Windows, in Rgui, added support for all languagesĀ listed by list.dirs(system.file(package = "translations"), full.names = FALSE, recursive = FALSE)
saving the normalized path within its appropriate environment the first time this.path is called within a script, making it faster to use subsequent times within the same script and being independent of working directory. This means that setwd will no longer break this.path when using relative paths within source or when running R from a shell (as long as setwd is used AFTER the first call to this.path within that script)
Original Answer:
My answer is an improvement upon Jerry T's answer. The issue I found is that they are guessing whether a source call was made by checking if variable ofile is found in the first frame on the stack. This will not work with nested source calls, nor source calls made from a non-global environment. Additionally, the order is wrong. We must look for source call BEFORE checking the shell arguments. Here is my solution:
this.path <- function (verbose = getOption("verbose"))
{
# loop through functions that lead here from most recent to
# earliest looking for an appropriate source call (a call to
# function source / / sys.source / / debugSource in RStudio)
#
# an appropriate source call is one in which the file argument has
# been evaluated (forced)
#
# for example, this means `source(this.path())` is an inappropriate
# source call. the argument 'file' is stored as a promise
# containing the expression "this.path()". when the value of 'file'
# is requested, the expression is evaluated at which time there
# should be two functions on the calling stack 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 'file' has
# already been evaluated, the easiest way of which is to ask if a
# variable exists, one which is only created after the expression
# is necessarily evaluated.
#
# if that variable does exist, then argument 'file' has been forced
# and the source call is deemed appropriate. For 'source', the
# filename we want is the variable 'ofile' from that function's
# evaluation environment. For '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
#
# 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 promise). 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
debugSource <- if (.Platform$GUI == "RStudio")
get("debugSource", "tools:rstudio", inherits = FALSE)
for (n in seq.int(to = 1L, by = -1L, length.out = sys.nframe() - 1L)) {
if (identical(sys.function(n), 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
if (verbose)
cat("Source: call to function source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), sys.source) &&
exists("exprs", envir = sys.frame(n), inherits = FALSE))
{
path <- get("file", envir = sys.frame(n), inherits = FALSE)
if (verbose)
cat("Source: call to function sys.source\n")
return(normalizePath(path, mustWork = TRUE))
}
else if (identical(sys.function(n), debugSource) &&
tryCatch({
path <- get("fileName", envir = sys.frame(n), inherits = FALSE)
TRUE
}, error = function(c) FALSE))
{
if (verbose)
cat("Source: call to function debugSource in RStudio\n")
return(normalizePath(path, mustWork = TRUE))
}
}
# no appropriate source call was found up the calling stack
# if (running R from RStudio)
if (.Platform$GUI == "RStudio") {
# ".rs.api.getActiveDocumentContext" from "tools:rstudio"
# returns a list of information about the document where your
# cursor is located
#
# ".rs.api.getSourceEditorContext" from "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
context <- get(".rs.api.getActiveDocumentContext",
"tools:rstudio", inherits = FALSE)()
active <- context[["id"]] != "#console"
if (!active) {
context <- get(".rs.api.getSourceEditorContext",
"tools:rstudio", inherits = FALSE)()
if (is.null(context))
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\n",
" (or source document has no path)")
}
path <- context[["path"]]
Encoding(path) <- "UTF-8"
if (nzchar(path)) {
if (verbose)
cat(if (active)
"Source: active document in RStudio\n"
else "Source: source document in RStudio\n")
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",
if (active)
"* active document in RStudio does not exist"
else "* source document in RStudio does not exist")
}
# if (running R from RStudio before .Platform$GUI is changed)
# this includes code evaluated in the site-wide startup profile file,
# user profile, and function .First (see ?Startup)
else if (isTRUE(Sys.getpid() == as.integer(Sys.getenv("RSTUDIO_SESSION_PID"))) {
stop("RStudio has not finished loading")
}
# if (running R from a shell)
else if (.Platform$OS.type == "windows" && .Platform$GUI == "RTerm" || # on Windows
.Platform$OS.type == "unix" && .Platform$GUI == "X11") # under Unix-alikes
{
argv <- commandArgs()
# remove all trailing arguments
m <- match("--args", argv, 0L)
if (m)
argv <- argv[seq_len(m)]
argv <- argv[-1L]
# get all arguments starting with "--file="
FILE <- argv[startsWith(argv, "--file=")]
# remove "--file=" from the start of each string
FILE <- substring(FILE, 8L)
# remove strings "-"
FILE <- FILE[FILE != "-"]
n <- length(FILE)
if (n) {
FILE <- FILE[[n]]
if (verbose)
cat("Source: shell argument 'FILE'\n")
return(normalizePath(FILE, mustWork = TRUE))
} 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")
}
}
# if (running R from RGui on Windows)
else if (.Platform$OS.type == "windows" && .Platform$GUI == "Rgui") {
# "getWindowsHandles" from "utils" (Windows exclusive) returns
# a list of external pointers containing the windows handles.
# The thing of interest are the names of this list, these are
# 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 start with "R Console" or end with
# " - R Editor". From there, similar checks are done as in the
# above section for 'RStudio'
x <- names(utils::getWindowsHandles(pattern = "^R Console| - R Editor$",
minimized = TRUE))
if (!length(x))
stop("no windows in Rgui; should never happen, please report!")
active <- !startsWith(x[[1L]], "R Console")
if (active)
x <- x[[1L]]
else if (length(x) >= 2L)
x <- x[[2L]]
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")
if (x == "Untitled - R Editor")
stop("'this.path' used in an inappropriate fashion\n",
"* no appropriate source call was found up the calling stack\n",
if (active)
"* active document in Rgui does not exist"
else "* source document in Rgui does not exist")
path <- sub(" - R Editor$", "", x)
if (verbose)
cat(if (active)
"Source: active document in Rgui\n"
else "Source: source document in Rgui\n")
return(normalizePath(path, mustWork = TRUE))
}
# if (running R from RGui on macOS)
else if (.Platform$OS.type == "unix" && .Platform$GUI == "AQUA") {
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 is currently unimplemented\n",
" consider using RStudio until such a time when this is implemented")
}
# otherwise
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")
}
A slimmed down variant of Supressingfire's answer:
source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}
This works for me. Just greps it out of the command line arguments, strips off the unwanted text, does a dirname and finally gets the full path from that:
args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
I have wrapped up and extended the answers to this question into a new function thisfile() in rprojroot. Also works for knitting with knitr.
I tried almost everything from this question, Getting path of an R script, Get the path of current script, Find location of current .R file and R command for setting working directory to source file location in Rstudio, but at the end found myself manually browsing the CRAN table and found
scriptName library
which provides current_filename() function, which returns proper full path of the script when sourcing in RStudio and also when invoking via R or RScript executable.
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))
}
}
}
I just worked this out myself. To ensure portability of your script always begin it with:
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.
Alternately, the following code performs the exact same thing:
wd <- getwd()
setwd(wd)
or, if you don't need the file path in an object you can simply:
setwd(".")
I also had this problem, and none of the above solutions worked for me. Maybe with the source or things like that, but it was not clear enough.
I found this, for me elegant, solution:
paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")
The important thing in that is the fileSnapshot() that gives you a lot of information about a file. It returns a list of 8 elements. When you pick path as the list element, the path is returned with \\ as separator, so the rest of the code is just to change that.
I hope this helps.
You can wrap the r script in a bash script and retrieve the script's path as a bash variable like so:
#!/bin/bash
# [environment variables can be set here]
path_to_script=$(dirname $0)
R --slave<<EOF
source("$path_to_script/other.R")
EOF
I like this approach:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
Note that the getopt package provides the get_Rscript_filename function, which just uses the same solution presented here, but is already written for you in a standard R module, so you don't have to copy and paste the "get script path" function into every script you write.
If rather than the script, foo.R, knowing its path location, if you can change your code to always reference all source'd paths from a common root then these may be a great help:
https://github.com/r-lib/rprojroot or https://rprojroot.r-lib.org/
https://here.r-lib.org/
Given
/app/deeply/nested/foo.R
/app/other.R
This will work
#!/usr/bin/env Rscript
library(here)
source(here("other.R"))
See https://rprojroot.r-lib.org/ for how to define project roots.
I had issues with the implementations above as my script is operated from a symlinked directory, or at least that's why I think the above solutions didn't work for me. Along the lines of #ennuikiller's answer, I wrapped my Rscript in bash. I set the path variable using pwd -P, which resolves symlinked directory structures. Then pass the path into the 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)
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))
}
})()
99% of the cases you might simply use:
sys.calls()[[1]] [[2]]
It will not work for crazy calls where the script is not the first argument, i.e., source(some args, file="myscript"). Use #hadley's in these fancy cases.
Steamer25's approach works, but only if there is no whitespace in the path. On macOS at least the cmdArgs[match] returns something like /base/some~+~dir~+~with~+~whitespace/ for /base/some\ dir\ with\ whitespace/.
I worked around this by replacing the "~+~" with a simple whitespace before returning it.
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))
}
}
Obviously you can still extend the else block like aprstar did.
By looking at the call stack we can get the filepath of each script being executed, the two most useful will probably either be the currently executing script, or the first script to be sourced (entry).
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) ))()
The solution arrived in 2016. Many thanks to the author, Sahil Seth!
The package funr on CRAN and github provides the function sys.script() which gets the full path to the current script. It even references a similar SO post.
Thus, the solution is:
myscript.R:
#!/usr/bin/env Rscript
f <- funr::sys.script()
show(f)
and then executing the command:
user#somewhere:/home$ Rscript myscript.R
at the command line will output, e.g.:
"/home/path/to/myscript.R"
to the console.
See findSourceTraceback() of the R.utils package, which
Finds all 'srcfile' objects generated by source() in all call frames.
This makes it possible to find out which files are currently scripted by source().
#!/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='/'))
Amazing there is no '$0' type structure in R! You can do it with a system() call to a bash script written in R:
write.table(c("readlink -e $0"), file="scriptpath.sh",col=F, row=F, quote=F)
thisscript <- system("sh scriptpath.sh", intern = TRUE)
Then just split out the scriptpath.sh name for other.R
splitstr <- rev(strsplit(thisscript, "\\/")[[1]])
otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")
I work in an HPC cluster environment. I develop my code in a different location from where I do my production runs. During development, I'm usually calling R interactively from the command line (not using RStudio). There is lots of source("foo.R") going on.
During production runs, I usually write a bash script that tries different parameters and runs each set of parameters in a separate directory. The bash script utilizes the workload manager (i.e. SLURM). In this environment, it is trivial to set an environmental variable. With this in mind, the below solution works best for me.
other.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)
If running this from the R interactive shell and within /path/to/R/code, simply do
> source("foo.R")
If running not from the interactive shell and not running from /path/to/R/code, set the environmental variable R_SRC first, then call Rscript
$ export R_SRC=/path/to/R/code/
$ Rscript /path/to/R/code/foo.R
Just to build on the above answers, as a safety check, you could add a wrapper that asks the user to find the file if (for whatever reason) sys.frame(1) fails (as it might if interactive() == TRUE), or the sourced script is not where the main script expects it to be.
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')