Rscript: determina il percorso dello script in esecuzione

Ho uno script chiamato foo.R che include un altro script other.R , che si trova nella stessa directory:

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

Ma voglio che R trovi other.R indipendentemente dalla directory di lavoro corrente.

In altre parole, foo.R bisogno di conoscere il proprio percorso. Come lo posso fare?

Qui c’è una soluzione semplice per il problema. Questo comando:

 script.dir <- dirname(sys.frame(1)$ofile) 

restituisce il percorso del file di script corrente. Funziona dopo che lo script è stato salvato.

È ansible utilizzare la funzione commandArgs per ottenere tutte le opzioni che sono state passate da Rscript all’interprete R reale e cercarle per --file= . Se il tuo script è stato avviato dal percorso o se è stato avviato con un percorso completo, lo script.name seguito inizierà con un '/' . Altrimenti, deve essere relativo al cwd e puoi cwd i due percorsi per ottenere il percorso completo.

Modifica: sembra che avresti solo bisogno di script.name sopra e di rimuovere il componente finale del percorso. Ho rimosso l’esempio cwd() non necessario e ripulito lo script principale e pubblicato il mio other.R . Basta salvare questo script e lo script other.R nella stessa directory, chmod +x , ed eseguire lo script principale.

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) 

altro.R :

 print("hello") 

uscita :

 [email protected]:~$ main.R [1] "Sourcing /home/burner/bin/other.R from /home/burner/bin/main.R" [1] "hello" [email protected]:~$ bin/main.R [1] "Sourcing bin/other.R from bin/main.R" [1] "hello" [email protected]:~$ cd bin [email protected]:~/bin$ main.R [1] "Sourcing ./other.R from ./main.R" [1] "hello" 

Questo è ciò che credo che Dehmann stia cercando.

Non sono riuscito a far funzionare la soluzione di Suppressingfire quando “source’ing dalla console R”.
Non riuscivo a far funzionare la soluzione di Hadley quando usavo Rscript.

Il meglio di entrambi i mondi?

 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)]]) 

Non chiedermi come funziona, perché ho dimenticato: /

Una variante snellita della risposta di Supressingfire:

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

Questo funziona per me

 library(rstudioapi) rstudioapi::getActiveDocumentContext()$path 

La risposta dei rakensi di Getting path of a R script è l’IMHO più corretta e davvero brillante. Eppure, è ancora un hack che incorpora una funzione fittizia. Lo sto citando qui, per poterlo trovare più facilmente da altri.

sourceDir <- getSrcDirectory (function (dummy) {dummy})

Questo fornisce la directory del file in cui è stata inserita l’istruzione (in cui è definita la funzione fittizia). Può quindi essere utilizzato per impostare il direttorio di lavoro e utilizzare percorsi relativi ad es

 setwd(sourceDir) source("other.R") 

o per creare percorsi assoluti

  source(paste(sourceDir, "/other.R", sep="")) 

Questo funziona per me. Lo toglie di mezzo dagli argomenti della riga di comando, toglie il testo indesiderato, fa un dirname e alla fine ottiene il percorso completo da quello:

 args <- commandArgs(trailingOnly = F) scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)]))) 

Tutto in uno!

 #' current script file (in full path) #' @param #' @return #' @examples #' works with Rscript, source() or in RStudio Run selection #' @export 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 return(normalizePath(rstudioapi::getActiveDocumentContext()$path)) } } } } 

L’ho appena scoperto. Per garantire che la portabilità del tuo script inizi sempre con:

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

Funziona perché "." traduce come il comando Unix $ PWD. Assegnare questa stringa a un object carattere consente di inserire l'object carattere in setwd () e Presto il codice verrà sempre eseguito con la sua directory corrente come directory di lavoro, indipendentemente dalla macchina su cui si trova o dalla struttura del file in cui si trova trova. (Bonus extra: l'object wd può essere utilizzato con file.path () (ad es. File.path (wd, "directory_destinazione") per consentire la creazione di una directory di output standard indipendentemente dal percorso del file che porta alla directory indicata. Ciò richiede che tu crei la nuova directory prima di riferirla in questo modo, ma anche quella può essere aiutata con l'object wd.

In alternativa, il codice seguente esegue esattamente la stessa cosa:

 wd <- getwd() setwd(wd) 

oppure, se non hai bisogno del percorso del file in un object, puoi semplicemente:

 setwd(".") 

Mi è piaciuta la soluzione di steamer25 in quanto sembra la più robusta per i miei scopi. Tuttavia, durante il debug in RStudio (in Windows), il percorso non viene impostato correttamente. Il motivo è che se in RStudio è impostato un punto di interruzione, il file utilizza un comando alternativo “origine debug” che imposta il percorso dello script in modo leggermente diverso. Ecco la versione finale che sto usando attualmente per spiegare questo comportamento alternativo all’interno di RStudio durante il debug:

 # @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)) } } } 

Ho concluso ed esteso le risposte a questa domanda in una nuova funzione thisfile() in rprojroot . Funziona anche per lavorare a maglia con knitr .

Mi piace questo approccio:

 this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile this.dir <- dirname(this.file) 

Nota che il pacchetto getopt fornisce la funzione get_Rscript_filename , che usa solo la stessa soluzione presentata qui, ma è già stata scritta per te in un modulo R standard, quindi non devi copiare e incollare la funzione “get script path” in ogni sceneggiatura che scrivi

Puoi avvolgere lo script r in uno script bash e recuperare il percorso dello script come una variabile bash in questo modo:

 #!/bin/bash # [environment variables can be set here] path_to_script=$(dirname $0) R --slave< 

Vedi findSourceTraceback() del pacchetto R.utils , che

Trova tutti gli oggetti ‘srcfile’ generati da source () in tutti i frame di chiamata. Ciò rende ansible scoprire quali file sono attualmente scriptati da source ().

Ho avuto problemi con le implementazioni precedenti dato che il mio script è gestito da una directory symlinked, o almeno è per questo che penso che le soluzioni di cui sopra non hanno funzionato per me. Sulla falsariga della risposta di @ ennuikiller, ho avvolto il mio Rscript in bash. Ho impostato la variabile path usando pwd -P , che risolve strutture di directory symlinked. Quindi passare il percorso in 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) 

Userei una variante dell’approccio di @ steamer25. Il punto è che preferisco ottenere l’ultimo script di origine anche quando la mia sessione è stata avviata tramite Rscript. Lo snippet seguente, quando incluso in un file, fornirà una variabile di questo thisScript contiene il percorso normalizzato dello script. Confesso l’uso (ab) del sourceing, quindi a volte invoco Rscript e lo script fornito nell’argomento --file sources un altro script che ne --file un altro … Un giorno investirò nel trasformare il mio codice disordinato in un pacchetto .

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

Il 99% dei casi che potresti semplicemente usare:

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

Non funzionerà per le chiamate pazze in cui lo script non è il primo argomento, vale a dire, source(some args, file="myscript") . Usa @ Hadley in questi casi fantasiosi.

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

L’approccio di Steamer25 funziona, ma solo se non ci sono spazi bianchi nel percorso. Su macOS almeno cmdArgs[match] restituisce qualcosa come /base/some~+~dir~+~with~+~whitespace/ per /base/some\ dir\ with\ whitespace/ .

Ho lavorato attorno a questo sostituendo “~ + ~” con uno spazio bianco semplice prima di restituirlo.

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

Ovviamente puoi ancora estendere il blocco come ha fatto aprstar.

Se piuttosto che la sceneggiatura, foo.R , conoscendo la sua posizione sul percorso, se puoi cambiare il tuo codice per fare sempre riferimento a tutti i percorsi d’origine di una root comune, questi possono essere di grande aiuto:

Dato

  • /app/deeply/nested/foo.R
  • /app/other.R

Questo funzionerà

 #!/usr/bin/env Rscript library(here) source(here("other.R")) 

Vedere https://krlmlr.github.io/rprojroot/ per come definire le radici del progetto.

Incredibile non esiste una struttura di tipo “$ 0” in R! Puoi farlo con una chiamata system () ad uno script bash scritto in R:

 write.table(c("readlink -e $0"), file="scriptpath.sh",col=F, row=F, quote=F) thisscript <- system("sh scriptpath.sh", intern = TRUE) 

Quindi basta dividere il nome scriptpath.sh per other.R

 splitstr <- rev(strsplit(thisscript, "\\/")[[1]]) otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")