Rscript: déterminer le chemin du script d'exécution

j'ai un script appelé foo.R qui inclut un autre script other.R , qui est dans le même répertoire:

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

mais je veux que R trouve que other.R peu importe le répertoire de travail actuel.

en d'autres termes, foo.R a besoin de connaître sa propre voie. Comment puis-je le faire?

200
demandé sur MichaelChirico 2009-11-29 16:58:32

23 réponses

ici il y a une solution simple pour le problème. Cette commande:

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

renvoie le chemin du fichier script actuel. Ça marche après que le script ait été sauvegardé.

77
répondu this.is.not.a.nick 2013-04-17 00:42:42

vous pouvez utiliser la fonction commandArgs pour obtenir toutes les options qui ont été 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 au cwd et vous pouvez concater les deux chemins pour obtenir le chemin complet.

Edit: , ça sonne comme vous le feriez vous n'avez besoin que du script.name ci-dessus et pour enlever la composante finale du chemin. J'ai retiré l'échantillon inutile de cwd() et j'ai nettoyé le script principal et posté mon other.R . Il suffit de sauvegarder ce script et le script other.R dans le même répertoire, chmod +x eux, et d'exécuter le script principal.

principal.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)

autres.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 crois que dehmann cherche.

54
répondu Suppressingfire 2018-08-23 18:10:38

Je ne pouvais pas obtenir la solution de Suppressingfire pour fonctionner quand 'source' à partir de la console R.

Je n'arrivais pas à faire fonctionner la solution d'hadley en utilisant Rscript.

"151910920 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))
        }
}
38
répondu steamer25 2014-11-13 16:35:24
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é: /

32
répondu hadley 2009-11-29 19:21:05

Cela fonctionne pour moi

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
13
répondu ColinTea 2017-08-23 15:38:52

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
répondu momeara 2011-06-23 23:31:06

La réponse de rakensi à partir de Obtenir le chemin d'un script R est plus correct et vraiment génial à mon humble avis. Pourtant, c'est toujours un piratage avec une fonction factice. Je cite ici, afin d'avoir plus facilement trouvé par les autres.

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

donne le répertoire du fichier où la déclaration a été placée (où la fonction du mannequin est définie). Il peut ensuite être utilisé pour définir la direction de travail et d'utiliser des chemins relatifs par exemple

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

ou pour créer des chemins absolus

 source(paste(sourceDir, "/other.R", sep=""))
11
répondu cuffel 2016-03-29 05:39:18

ça me va. Il suffit de le retirer des arguments de la ligne de commande, de supprimer le texte indésirable, de faire un dirname et obtient finalement le chemin complet à partir de cela:

args <- commandArgs(trailingOnly = F)  
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
10
répondu eddi 2013-06-18 19:05:56

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))
            }
        }
    }
}
8
répondu Jerry T 2016-04-21 18:24:36

je viens de m'en rendre compte moi-même. Pour assurer 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 objet de caractères vous permet alors d'insérer cet objet de caractères dans setwd () et Presto votre code sera toujours exécuté avec son répertoire courant comme le répertoire de travail, quelle que soit la machine sur laquelle il se trouve ou l'endroit dans la structure du fichier où il se trouve. hébergé. (Bonus supplémentaire: l'objet wd peut être utilisé avec le fichier.path () (c.-à-d. fichier.chemin (wd," output_directory") pour permettre la création d'un répertoire de sortie standard quel que soit le chemin du fichier menant à votre répertoire nommé. Cela nécessite que vous fassiez le nouveau répertoire avant de le référencer de cette façon, mais cela aussi peut être aidé avec l'objet wd.

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

wd <- getwd()
setwd(wd)

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

setwd(".")
7
répondu Andrew Moffat Jr. 2016-08-06 01:46:55

j'ai aimé la solution de steamer25 car elle semble la plus robuste pour mes besoins. Cependant, lors du débogage dans RStudio (sous windows), le chemin ne serait pas défini correctement. La raison étant que si un point de rupture est défini dans RStudio, le sourcing du fichier utilise une autre commande "debug source" qui définit le chemin du script un peu différemment. Voici la version finale que j'utilise actuellement et qui rend compte de cet autre comportement 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))
        }
    }
}
6
répondu aprstar 2015-08-14 18:46:17

j'ai enveloppé et étendu les réponses à cette question dans une nouvelle fonction thisfile() dans rprojroot . Fonctionne également pour le tricot avec knitr .

5
répondu krlmlr 2018-05-30 19:35:33

j'aime cette approche:

this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
2
répondu kuna.matata 2015-06-17 15:13:44

notez que le paquet getopt fournit la fonction get_Rscript_filename , qui utilise juste la même solution présentée ici, mais qui est déjà écrite pour vous dans un module R standard, de sorte que vous n'avez pas à copier et coller la fonction" get script path " dans chaque script que vous écrivez.

2
répondu Ryan Thompson 2017-03-15 21:13:59

vous pouvez envelopper le script r dans un script bash et récupérer le chemin du script comme une variable bash comme ceci:

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

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

     EOF
1
répondu ennuikiller 2009-11-29 14:49:04

voir findSourceTraceback() du R. utils colis, quel

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

1
répondu HenrikB 2014-05-18 17:02:56

j'ai eu des problèmes avec les implémentations ci-dessus car mon script est exploité à partir d'un répertoire symlinked, ou du moins c'est pourquoi je pense que les solutions ci-dessus ne fonctionnaient pas pour moi. Dans le sens de la réponse de @ennuikiller, j'ai enveloppé mon texte dans bash. J'ai défini la variable path en utilisant pwd -P , qui résout les structures de répertoires en symbiose. 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
répondu Luke Singham 2015-05-27 06:56:40

j'utiliserais une variante de l'approche de @steamer25. Le fait est que je préfère obtenir le script de la dernière source même lorsque ma session a commencé par Rscript. L'extrait suivant, lorsqu'il est inclus dans un fichier, fournira une variable thisScript contenant le chemin normalisé du script. Je confesse l'utilisation (ab) de source'ing, donc parfois j'invoque Rscript et le script fourni dans l'argument --file sources un autre script qui sources un autre... Un jour je le ferai investir pour faire de mon code désordonné 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))
  }
})()
1
répondu Ailton Andrade de Oliveira 2015-09-02 13:23:13

99% des cas que vous pourriez simplement utiliser:

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

il ne fonctionnera pas pour les appels fous où le script n'est pas le premier argument, i.e., source(some args, file="myscript") . Utilisez @hadley's dans ces belles affaires.

1
répondu antonio 2016-11-25 13:13:47
#!/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
répondu wildloop 2014-11-23 14:35:01

Steamer25 approche fonctionne, mais seulement si il n'y a pas d'espace dans le chemin d'accès. Sur macOS au moins le cmdArgs[match] renvoie quelque chose comme /base/some~+~dir~+~with~+~whitespace/ pour /base/some\ dir\ with\ whitespace/ .

j'ai contourné ce problème en remplaçant le "~ + ~ " par un simple espace blanc avant de le retourner.

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 encore étendre le bloc else comme aprstar l'a fait.

0
répondu iball 2017-07-29 12:37:54

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

Donné

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

Cela fonctionne

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

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

0
répondu mmell 2018-01-05 19:22:44

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

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

alors il suffit de diviser le scriptpath.sh nom pour autre.R

splitstr <- rev(strsplit(thisscript, "\/")[[1]])
otherscript <- paste0(paste(rev(splitstr[2:length(splitstr)]),collapse="/"),"/other.R")
0
répondu bruce.moran 2018-03-13 12:34:51