web-dev-qa-db-fra.com

Déterminer 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
message("Hello")
source("other.R")

Mais je veux que R trouve ce other.R quel que soit le répertoire de travail actuel.

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

226
Frank

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

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

renvoie le chemin du fichier de script actuel. Cela fonctionne après la sauvegarde du script.

90
this.is.not.a.nick

Vous pouvez utiliser la fonction commandArgs pour obtenir toutes les options passées par Rscript à l’interpréteur R réel et les rechercher pour --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 à la cwd et vous pouvez concaténer les deux chemins pour obtenir le chemin complet.

Modifier: il semblerait que vous n'ayez besoin que du script.name ci-dessus et que vous supprimiez le dernier composant du chemin. J'ai enlevé l'exemple inutile cwd(), nettoyé le script principal et posté mon other.R. Enregistrez 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.

64
Suppressingfire

Je ne pouvais pas obtenir la solution de Suppressingfire au travail quand 'source'ing de la console R.
La solution de Hadley ne fonctionnait pas 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))
        }
}
53
steamer25
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 demandez pas comment ça marche, car j'ai oublié: /

35
hadley

La réponse de rakensi à partir de Obtenir le chemin d'un script R est l'IMHO le plus correct et le plus brillant qui soit. Pourtant, c'est toujours un bidouillage intégrant une fonction fictive. Je le cite ici, afin que tout le monde le trouve plus facilement.

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

Cela donne le répertoire du fichier où l’instruction a été placée (où la fonction fictive 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=""))
24
cuffel

Ça marche pour moi

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

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="/"))
}
12
momeara

Mon tout en un! (--01/09/2019 mis à jour pour prendre en charge 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)
                }
            }
        }
    }
}
11
Jerry T

Cela fonctionne pour moi. Il suffit de le supprimer des arguments de la ligne de commande, de supprimer le texte indésirable, d'effectuer un nom de répertoire et d'obtenir finalement le chemin complet:

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

J'ai presque tout essayé de cette question, Obtenir le chemin d'un script R , Obtenir le chemin du script actuel , Trouver l'emplacement du fichier .R actuel et commande R permettant de définir le répertoire de travail sur l'emplacement du fichier source dans Rstudio , mais je me suis finalement retrouvé manuellement parcourant la table CRAN et trouvé

scriptName bibliothèque

qui fournit la fonction current_filename(), qui renvoie le chemin complet du script lors de l’approvisionnement en RStudio et lors de l’appel via l’exécutable R ou RScript.

6
Bojan P.

J'ai aimé la solution de steamer25 car elle me semble la plus robuste. Cependant, lors du débogage dans RStudio (sous 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 du script un peu différemment. Voici la version finale que j'utilise actuellement et qui explique ce comportement alternatif au sein de 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))
        }
    }
}
6
aprstar

J'ai résumé et étendu les réponses à cette question dans une nouvelle fonction thisfile() in rprojroot . Fonctionne également pour le tricotage avec knitr.

6
krlmlr

Je viens de résoudre ce problème moi-même. Pour garantir la portabilité de votre script, commencez toujours par:

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

Cela fonctionne parce que "." traduit comme la commande Unix $ PWD. L'affectation de cette chaîne à un objet caractère vous permet d'insérer cet objet caractère dans setwd () et Presto votre code sera toujours exécuté avec son répertoire actuel comme répertoire de travail, quelle que soit la machine sur laquelle il est ou où il se trouve dans la structure du fichier. (Bonus supplémentaire: l’objet wd peut être utilisé avec file.path () (c’est-à-dire file.path (wd, "répertoire_sortie") pour permettre la création d’un répertoire de sortie standard quel que soit le chemin du fichier menant au répertoire nommé. Cela nécessite que vous créiez le nouveau répertoire avant de le référencer de cette façon, mais cela peut également être facilité avec l’objet wd.

Sinon, le code suivant effectue exactement la même chose:

wd <- getwd()
setwd(wd)

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

setwd(".")
4
Andrew Moffat Jr.

Vous pouvez envelopper le script r dans un script bash et récupérer le chemin du script en tant que variable bash comme ceci:

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

     R --slave<<EOF
        source("$path_to_script/other.R")

     EOF
2
ennuikiller

Notez que le paquet 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 le chemin "get script". "Fonction dans chaque script que vous écrivez.

2
Ryan Thompson

J'aime cette approche:

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

Voir findSourceTraceback() du paquet R.utils , qui

Recherche tous les objets 'srcfile' générés par source () dans tous les cadres d'appel. Cela permet de savoir quels fichiers sont actuellement scriptés par source ().

1
HenrikB

Dans 99% des cas, vous pourriez simplement utiliser:

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

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

1
antonio

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. L'extrait suivant, lorsqu'il est inclus dans un fichier, fournira une variable thisScript contenant le chemin d'accès normalisé du script. J'avoue utiliser (ab) l'utilisation de source, donc parfois j'appelle Rscript et le script fourni dans l'argument --file un autre script qui en source un autre ... Un jour, j'investirai pour que mon code en désordre se transforme en 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))
  }
})()

J'ai eu des problèmes avec les implémentations ci-dessus car mon script est exploité à partir d'un répertoire lié symboliquement, ou du moins c'est pourquoi je pense que les solutions ci-dessus ne m'ont pas fonctionné. Dans le sens de la réponse de @ ennuikiller, j'ai enveloppé mon Rscript dans bash. J'ai défini la variable de chemin à l'aide de pwd -P, qui résout les structures de répertoire liées par des liens symboliques. Puis passez le chemin dans le 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)
1
Luke Singham

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, divisez simplement le nom scriptpath.sh pour other.R

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

L’approche de Steamer25 fonctionne, mais seulement s’il n’ya pas d’espace dans le chemin. Sur macOS au moins, le cmdArgs[match] renvoie quelque chose comme /base/some~+~dir~+~with~+~whitespace/ pour /base/some\ dir\ with\ whitespace/.

J'ai travaillé autour de 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))
  }
}

Évidemment, vous pouvez toujours prolonger le bloc else comme l'a fait aprstar.

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

Si plutôt que le script, foo.R, connaissant l'emplacement de son chemin, si vous pouvez changer votre code pour toujours référencer tous les chemins source 'd d'un commun root, ils peuvent être d'une grande aide. :

Donné

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

Ça va marcher

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

Voir https://rprojroot.r-lib.org/ pour savoir comment définir les racines du projet.

0
mmell

En examinant la pile d'appels, nous pouvons obtenir le chemin d'accès à chaque script en cours d'exécution. Les deux plus utiles seront probablement soit le script en cours d'exécution, soit le premier script à obtenir (entrée).

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) ))()
0
user425678