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?
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é.
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.
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.
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é: /
Cela fonctionne pour moi
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
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="/"))
}
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=""))
ç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)])))
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 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(".")
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))
}
}
}
j'aime cette approche:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
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.
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
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)
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))
}
})()
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.
#!/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='/'))
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.
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.
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")