Rscript: determina la ruta del script de ejecución

Tengo un script llamado foo.R que incluye otro script other.R , que está en el mismo directorio:

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

Pero quiero que R encuentre ese other.R sin importar el directorio de trabajo actual.

En otras palabras, foo.R necesita saber su propio camino. ¿Cómo puedo hacer eso?

Aquí hay una solución simple para el problema. Este comando:

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

devuelve la ruta del archivo de script actual. Funciona después de que se guardó la secuencia de comandos.

Puede usar la función commandArgs para obtener todas las opciones que pasó Rscript al intérprete real de R y buscarlas en --file= . Si su secuencia de comandos se inició desde la ruta o si se inició con una ruta completa, el script.name continuación comenzará con un '/' . De lo contrario, debe ser relativo a la cwd y puede concat con las dos rutas para obtener la ruta completa.

Editar: parece que solo necesitaría el script.name anterior y para quitarle el componente final de la ruta. cwd() muestra de cwd() innecesaria y other.R el script principal y other.R mi other.R Simplemente guarde este script y el script other.R en el mismo directorio, chmod +x ellos, y ejecute el script principal.

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) 

otro.R :

 print("hello") 

salida :

 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" 

Esto es lo que creo que dehmann está buscando.

No pude conseguir que la solución de Suppressingfire funcionara cuando se ‘originaba’ desde la consola R.
No pude lograr que la solución de Hadley funcionara cuando usé Rscript.

¿Lo mejor de ambos mundos?

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

No me preguntes cómo funciona, porque lo he olvidado:

Una variante adelgazada de la respuesta de Supressingfire:

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

Esto funciona para mí

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

La respuesta de rakensi de Getting path of a R script es la más correcta y realmente shiny en mi humilde opinión. Sin embargo, sigue siendo un truco que incorpora una función ficticia. Lo estoy citando aquí, para que otros lo encuentren más fácilmente.

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

Esto proporciona el directorio del archivo donde se colocó la statement (donde se define la función ficticia). Luego se puede usar para configurar el directorio de trabajo y usar rutas relativas, por ejemplo

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

o para crear rutas absolutas

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

Esto funciona para mí Simplemente lo elimina de los argumentos de la línea de comando, quita el texto no deseado, hace un nombre de directorio y finalmente obtiene la ruta completa de eso:

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

¡Todo en 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)) } } } } 

Solo resolví esto yo mismo. Para garantizar la portabilidad de su script, siempre comience con:

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

Funciona porque "." se traduce como el comando Unix $ PWD. Asignar esta cadena a un objeto de carácter le permite luego insertar ese objeto de carácter en setwd () y Prest su código siempre se ejecutará con su directorio actual como el directorio de trabajo, sin importar en qué máquina esté o en la estructura de archivos situado. (Bonificación extra: el objeto wd se puede usar con file.path () (es decir, file.path (wd, "output_directory") para permitir la creación de un directorio de salida estándar, independientemente de la ruta del archivo que conduce a su directorio nombrado. Esto requiere que hagas el nuevo directorio antes de hacer referencia de esta manera, pero eso también puede ser ayudado con el objeto wd.

Alternativamente, el siguiente código realiza exactamente lo mismo:

 wd <- getwd() setwd(wd) 

o, si no necesita la ruta del archivo en un objeto, simplemente puede:

 setwd(".") 

Me gustó la solución de steamer25 ya que parece ser la más robusta para mis propósitos. Sin embargo, al depurar en RStudio (en Windows), la ruta no se establecería correctamente. La razón es que si se establece un punto de interrupción en RStudio, el origen del archivo utiliza un comando alternativo de “fuente de depuración” que establece la ruta del script de forma un poco diferente. Aquí está la versión final que estoy usando y que explica este comportamiento alternativo dentro de RStudio cuando se depura:

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

He completado y ampliado las respuestas a esta pregunta en una nueva función thisfile() en rprojroot . También funciona para tejer con knitr .

Me gusta este enfoque:

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

Tenga en cuenta que el paquete getopt proporciona la función get_Rscript_filename , que solo usa la misma solución presentada aquí, pero ya está escrita para usted en un módulo R estándar, por lo que no tiene que copiar y pegar la función “get script path” en cada script que escribes

Puede envolver el guión r en un script bash y recuperar la ruta del guión como una variable bash como esta:

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

Consulte findSourceTraceback() del paquete R.utils , que

Encuentra todos los objetos ‘srcfile’ generados por source () en todos los marcos de llamadas. Esto permite averiguar qué archivos están actualmente guionados por source ().

Tuve problemas con las implementaciones anteriores ya que mi script funciona desde un directorio enlazado, o al menos es por eso que creo que las soluciones anteriores no me funcionaron. En la línea de la respuesta de @ennuikiller, envolví mi Rscript en bash. Establecí la variable de ruta usando pwd -P , que resuelve las estructuras de directorios enlazadas. Luego pasa el camino hacia el 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) 

Usaría una variante del enfoque de @ steamer25. El punto es que prefiero obtener el último script fuente incluso cuando mi sesión se inició a través de Rscript. El siguiente fragmento, cuando se incluye en un archivo, proporcionará una variable thisScript contiene la ruta normalizada del script. Confieso el uso (ab) de la fuente, por lo que a veces invoco a Rscript y el script proporcionado en el argumento --file fonts de otro script que proporciona otro … Algún día invertiré en hacer que mi desordenado código se convierta en un paquete .

 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% de los casos que podrías usar simplemente:

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

No funcionará para llamadas locas donde el guión no es el primer argumento, es decir, source(some args, file="myscript") . Usa @ hadley en estos casos elegantes.

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

El enfoque de Steamer25 funciona, pero solo si no hay espacios en blanco en el camino. En macOS, al menos, el cmdArgs[match] devuelve algo como /base/some~+~dir~+~with~+~whitespace/ for /base/some\ dir\ with\ whitespace/ .

Trabajé alrededor de esto reemplazando el “~ + ~” con un espacio en blanco simple antes de devolverlo.

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

Obviamente, puedes extender el bloque else como lo hizo aprstar.

Si en lugar de la secuencia de comandos, foo.R , conociendo su ubicación de ruta, si puede cambiar su código para siempre hacer referencia a todas las rutas source desde una root común, estas pueden ser de gran ayuda:

Dado

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

Esto funcionará

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

Consulte https://krlmlr.github.io/rprojroot/ para saber cómo definir las raíces del proyecto.

¡Increíble no hay estructura de tipo ‘$ 0’ en R! Puede hacerlo con una llamada a system () a un script bash escrito en R:

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

Luego simplemente divide el nombre de scriptpath.sh para other.R

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