here() issue in R scripts
Although your question requires the usage of the here
package, I propose a solution without the need of it. I think that it's much cleaner and equally portable.
If my understanding is correct, you want your script to be aware of their location. This is fine, but in most cases unnecessary because the caller of your script must know where the script is located to actually call it and you can exploit this knowledge. So:
- get rid of all the
here
calls; - don't try to determine in your scripts the file location, but just write every path as relative to the root of your folder (just as you do in development).
Next, a couple of options.
The first, minimal, is just not to register to cron
the bare Rstudio /path/to/yourfolder/yourscript.R
, but rather create a bash script as follow (let's call it script.sh
):
#!/bin/sh
cd /path/to/yourfolder
Rscript yourscript.R
and register this script to crontab
. You can add to your folder a README
file when you instruct to do the above (something like: "extract the folder wherever you want, take note of the path, build a script.sh
file and crotab it"). Of course, with Rstudio you can open and run the file in the usual way (setwd
and then run it; you document it in the README
).
The second is to write an "installer" (you can choose whether a makefile
, a simple R script, bash file or whatever), that does the above automatically. It just performs these steps.
- Creates a folder under the homedir, something like
.robertsProject
(notice the dot to be more likely that the directory does not exist). - Copies all the files and directories from your folder to this newly created folder.
- Creates a
.sh
file just as the one above (notice that you know where you are moving the files and their location, so you can write the correct path in the script). - Registers the
.sh
file to crontab.
Done! Whoever receive the file will have just to run once this installer (you will document how to do it in the README) and they can use your tool.
what you asked for
The behaviour of here()
isn't really what you want here, I think. Instead, what you are looking for is to determine the path of the source file aka the .R
file. I extended the here()
command a little to behave the way you expect:
here2 <- function() {
args <- commandArgs(trailingOnly = FALSE)
if ("RStudio" %in% args) {
dirname(rstudioapi::getActiveDocumentContext()$path)
} else {
file_arg <- "--file="
filepath <- sub(file_arg, "", grep(file_arg, args, value = TRUE))
dirname(filepath)
}
}
The idea for the case when the script is not run in RStudio comes from this answer. I tried this by pasting the function definition at the beginning of your dataFetcherMailer.R
file. You could also think about putting this in another file in your home directory and call it with, e.g., source("here2.R")
instead of library(here)
or you could write a small R package for this purpose.
final version by r0berts (op)
here2 <- function() {
args <- commandArgs(trailingOnly = FALSE)
if ("RStudio" %in% args) {
# R script called from Rstudio with "source file button"
filepath <- rstudioapi::getActiveDocumentContext()$path
} else if ("--slave" %in% args) {
# Rmd file called from Rstudio with "knit button"
# (if we placed this function in a .Rmd file)
file_arg <- "rmarkdown::render"
string <- grep(file_arg, args, value = TRUE)
mBtwQuotes <- "(?<=')[^']*[^']*(?=')"
filepath <- regmatches(string,regexpr(mBtwQuotes,string,perl = T))
} else if ((sum(grepl("--file=" ,args))) >0) {
# called in some other way that passes --file= argument
# R script called via cron or commandline using Rscript
file_arg <- "--file="
filepath <- sub(file_arg, "", grep(file_arg, args, value = TRUE))
} else if (sum(grepl("rmarkdown::render" ,args)) >0 ) {
# Rmd file called to render from commandline with
# Rscript -e 'rmarkdown::render("RmdFileName")'
file_arg <- "rmarkdown::render"
string <- grep(file_arg, args, value = TRUE)
mBtwQuotes <- "(?<=\")[^\"]*[^\"]*(?=\")"
filepath <- regmatches(string,regexpr(mBtwQuotes,string,perl = T))
} else {
# we do not know what is happening; taking a chance; could have error later
filepath <- normalizePath(".")
return(filepath)
}
filepath <- dirname(filepath)
return(filepath)
}
what I think most people actually need
I found this way a while ago but then actually changed my workflow entirely to only use R Markdown files (and RStudio projects). One of the advantages of this is that the working directory of Rmd files is always the location of the file. So instead of bothering with setting a working directory, you can just write all paths in your script relative to the Rmd file location.
---
title: "Data collection control report"
author: "HAL"
date: "`r Sys.Date()`"
output: pdf_document
---
```{r setup, include=FALSE}
library(knitr)
# in actual program this reads data from a changing online data source
df.main <- mtcars
# data backup
datestamp <- format(Sys.time(),format="%Y-%m-%d_%H-%M")
# create bkp folder if it doesn't exist
if (!dir.exists(paste0("./bkp/"))) dir.create("./bkp/")
backupName <- paste0("./bkp/dataBackup_", datestamp, "csv.gz")
write.csv(df.main, gzfile(backupName))
```
# This is data collection report
Yesterday's data total records: `r nrow(df.main)`.
The current directory is `r getwd()`
Note that paths starting with ./
mean to start in the folder of the Rmd file. ../
means you go one level up. ../../
you go two levels up and so on. So if your Rmd file is in a folder called "scripts" in your root folder, and you want to save your data in a folder called "data" in your root folder, you write saveRDS(data, "../data/dat.RDS")
.
You can run the Rmd file from command line/cron with Rscript -e 'rmarkdown::render("/home/johannes/Desktop/myGoodScripts/dataFetcher.Rmd")'
.