Rscript: Détermine le chemin du script en cours d’exécution

J’ai un script appelé foo.R qui inclut un autre script other.R , qui se trouve dans le même répertoire:

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

Mais je veux que R en trouve other.R autre. Quel que soit le répertoire de travail actuel.

En d’autres termes, foo.R besoin de connaître son propre chemin. Comment puis je faire ça?

Ici, il existe une solution simple au problème. Cette commande:

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

renvoie le chemin du fichier script actuel. Il fonctionne après que le script a été enregistré.

Vous pouvez utiliser la fonction commandArgs pour obtenir toutes les options transmises par Rscript à l’interpréteur R et les rechercher avec --file= . Si votre script a été lancé à partir du chemin ou s’il a été lancé avec un chemin complet, le script.name ci-dessous commencera par un '/' . Sinon, il doit être relatif au cwd et vous pouvez concaténer les deux chemins pour obtenir le chemin complet.

Edit: il script.name que vous n’ayez besoin que du script.name ci-dessus et que vous script.name le composant final du chemin. J’ai enlevé le cwd() inutile et nettoyé le script principal et posté mon other.R . Sauvegardez simplement ce script et le script other.R dans le même répertoire, chmod +x et exécutez le 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) 

autre.R :

 print("hello") 

sortie :

 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" 

C'est ce que je pense que dehmann recherche.

Je ne pouvais pas faire en sorte que la solution de Suppressingfire fonctionne à partir de la console R.
Je ne pouvais pas faire fonctionner la solution de hadley avec Rscript.

Le meilleur des deux mondes?

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

Ne me demande pas comment ça marche, parce que j'ai oublié: /

Une variante allégée de la réponse 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="/")) } 

Cela fonctionne pour moi

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

La réponse de rakensi à partir du cheminement d’un script R est la plus correcte et la plus shinye à mon humble avis . Pourtant, c’est toujours un hack incorporant une fonction factice. Je le cite ici pour le trouver plus facile à trouver.

sourceDir <- getSrcDirectory (fonction (factice) {factice})

Cela donne le répertoire du fichier où l’instruction a été placée (où la fonction factice est définie). Il peut ensuite être utilisé pour définir le répertoire de travail et utiliser des chemins relatifs, par exemple

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

ou pour créer des chemins absolus

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

Cela fonctionne pour moi. Il suffit de l’écrire en dehors des arguments de la ligne de commande, de supprimer le texte indésirable, de faire un dirname et d’obtenir le chemin complet à partir de cela:

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

Mon tout en un!

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

Je viens de travailler ça moi-même. Pour garantir la portabilité de votre script, commencez toujours par:

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

Ça marche parce que "." traduit comme la commande Unix $ PWD. L'affectation de cette chaîne à un object de caractère vous permet d'insérer cet object de caractère dans setwd () et Presto. Votre code s'exécutera toujours avec son répertoire actuel en tant que répertoire de travail, quel que soit son ordinateur ou sa structure. situé. (Bonus supplémentaire: L'object wd peut être utilisé avec file.path (). Ie. File.path (wd, "output_directory") pour permettre la création d'un répertoire de sortie standard quel que soit le chemin d'access au répertoire nommé. Cela nécessite que vous fassiez le nouveau répertoire avant de le référencer de cette manière, mais cela peut aussi être aidé par l'object wd.

Alternativement, le code suivant exécute exactement la même chose:

 wd <- getwd() setwd(wd) 

ou, si vous n'avez pas besoin du chemin de fichier dans un object, vous pouvez simplement:

 setwd(".") 

J’ai aimé la solution du steamer25 car elle semble la plus robuste pour mes besoins. Cependant, lors du débogage dans RStudio (dans Windows), le chemin ne serait pas défini correctement. La raison en est que si un point d’arrêt est défini dans RStudio, la source du fichier utilise une autre commande “source de débogage” qui définit le chemin de script un peu différemment. Voici la version finale que j’utilise actuellement et qui tient compte de ce comportement alternatif dans RStudio lors du débogage:

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

J’ai terminé et étendu les réponses à cette question dans une nouvelle fonction thisfile() dans rprojroot . knitr également pour le sortingcotage avec knitr .

J’aime cette approche:

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

Notez que le paquetage getopt fournit la fonction get_Rscript_filename , qui utilise simplement la même solution que celle présentée ici, mais qui est déjà écrite pour vous dans un module R standard. Vous n’avez donc pas à copier et coller la fonction “get script path” dans chaque script que vous écrivez

Vous pouvez envelopper le script r dans un script bash et récupérer le chemin du script sous la forme d’une variable bash comme ceci:

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

Voir findSourceTraceback() du paquetage R.utils , qui

Trouve tous les objects ‘srcfile’ générés par source () dans tous les frameworks d’appel. Cela permet de savoir quels fichiers sont actuellement scriptés par source ().

J’ai eu des problèmes avec les implémentations ci-dessus, car mon script est exploité à partir d’un répertoire avec liens symboliques, ou du moins c’est pourquoi je pense que les solutions ci-dessus n’ont pas fonctionné pour moi. Dans la lignée de la réponse de @ ennuikiller, j’ai emballé mon script en bash. Je mets la variable path en utilisant pwd -P , qui résout les structures de répertoires en liens symboliques. Ensuite, passez le chemin dans le script.

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) 

J’utiliserais une variante de l’approche de @ steamer25. Le fait est que je préfère obtenir le dernier script source même lorsque ma session a été démarrée via Rscript. Le fragment suivant, inclus dans un fichier, fournira une variable thisScript contenant le chemin normalisé du script. J’avoue l’utilisation (ab) de la source, donc parfois j’invoque Rscript et le script fourni dans l’argument --file fournit un autre script qui en génère un autre … Un jour j’investirai pour que mon code désordonné devienne un paquet .

 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% des cas, vous pourriez simplement utiliser:

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

Cela ne fonctionnera pas pour les appels déjantés où le script n’est pas le premier argument, c.-à-d. source(some args, file="myscript") arguments source(some args, file="myscript") . Utilisez @ hadley dans ces cas fantaisie.

 #!/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’approche de Steamer25 fonctionne, mais seulement s’il n’y a pas d’espace dans le chemin. Sur macOS au moins, le cmdArgs[match] retourne quelque chose comme /base/some~+~dir~+~with~+~whitespace/ pour /base/some\ dir\ with\ whitespace/ .

J’ai contourné cela en remplaçant le “~ + ~” par un simple espace avant de le renvoyer.

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

Evidemment, vous pouvez toujours étendre le bloc comme l'a fait aprstar.

Si plutôt que le script, foo.R , connaissant son emplacement, si vous pouvez changer votre code pour toujours faire référence à tous les chemins source depuis une root commune, cela peut être très utile:

Donné

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

Cela fonctionnera

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

Voir https://krlmlr.github.io/rprojroot/ pour savoir comment définir les racines du projet.

Incroyable, il n’y a pas de structure de type ‘$ 0’ dans R! Vous pouvez le faire avec un appel system () à un script bash écrit en R:

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

Ensuite, séparez simplement le nom scriptpath.sh pour autre.R

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