Rscript: Determine o caminho do script em execução

Eu tenho um script chamado foo.R que inclui outro script other.R , que está no mesmo diretório:

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

Mas eu quero R para encontrar esse other.R Não importa qual o diretório de trabalho atual.

Em outras palavras, foo.R precisa conhecer seu próprio caminho. Como eu posso fazer isso?

Aqui há uma solução simples para o problema. Este comando:

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

retorna o caminho do arquivo de script atual. Funciona depois que o script foi salvo.

Você pode usar a function commandArgs para obter todas as opções que foram passadas pelo Rscript para o intérprete R real e procurá-las para --file= . Se o seu script foi iniciado a partir do caminho ou se foi iniciado com um caminho completo, o script.name abaixo começará com um '/' . Caso contrário, deve ser relativo ao cwd e você pode concatenar os dois caminhos para obter o caminho completo.

Edit: parece que você só precisa do script.name acima e retirar o componente final do caminho. Eu removi a amostra cwd() desnecessária e limpei o script principal e other.R meu other.R . Apenas salve esse script e o script other.R no mesmo diretório, chmod +x los, e execute o 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) 

other.R :

 print("hello") 

saída :

 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" 

Isso é o que eu acredito que dehmann está procurando.

Não consegui fazer com que a solução do Suppressingfire funcionasse ao ‘fazer o source’ing do console R.
Eu não consegui a solução do hadley funcionar ao usar o Rscript.

Melhor dos dois 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)]]) 

Não me pergunte como funciona, porque eu esqueci: /

Uma variante enxuta da resposta 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="/")) } 

Isso funciona para mim

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

A resposta de rakensi de Getting path of an script é a mais correta e realmente shiny IMHO. No entanto, ainda é um hack que incorpora uma function fictícia. Estou citando aqui, para que seja mais facilmente encontrado pelos outros.

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

Isto dá o diretório do arquivo onde a declaração foi colocada (onde a function dummy é definida). Ele pode então ser usado para definir o diretório de trabalho e usar caminhos relativos, por exemplo

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

ou para criar caminhos absolutos

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

Isso funciona para mim. Apenas retira os argumentos da linha de comando, retira o texto indesejado, faz um nome de diretório e finalmente obtém o caminho completo a partir dele:

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

Meu tudo em um!

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

Eu só trabalhei isso sozinho. Para garantir a portabilidade do seu script, comece sempre com:

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

Isso funciona porque "." traduz como o comando Unix $ PWD. Atribuir essa string a um object de caractere permite que você insira esse object de caractere em setwd () e Presto. Seu código sempre será executado com seu diretório atual como o diretório de trabalho, não importa de qual máquina esteja ou onde localizado. (Bônus extra: o object wd pode ser usado com file.path () (ou seja, file.path (wd, "output_directory") para permitir a criação de um diretório de saída padrão, independentemente do caminho do arquivo que leva ao diretório nomeado. Isso requer que você crie o novo diretório antes de referenciá-lo dessa maneira, mas isso também pode ser auxiliado com o object wd.

Como alternativa, o código a seguir executa exatamente a mesma coisa:

 wd < - getwd() setwd(wd) 

ou, se você não precisa do caminho do arquivo em um object, você pode simplesmente:

 setwd(".") 

Eu gostei da solução do steamer25, pois parece ser a mais robusta para os meus propósitos. No entanto, ao depurar no RStudio (no Windows), o caminho não seria definido corretamente. O motivo é que, se um ponto de interrupção for configurado no RStudio, o uso do arquivo por um comando alternativo “debug source”, que define o caminho do script de forma um pouco diferente. Aqui está a versão final que estou usando atualmente para explicar esse comportamento alternativo no RStudio durante a debugging:

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

Eu thisfile() e estendi as respostas para essa pergunta em uma nova function thisfile() no rprojroot . Também trabalha para tricotar com knitr .

Eu gosto dessa abordagem:

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

Observe que o pacote getopt fornece a function get_Rscript_filename , que usa apenas a mesma solução apresentada aqui, mas já está escrita para você em um módulo R padrão, portanto, não é necessário copiar e colar a function “get script path” em cada roteiro que você escreve.

Você pode envolver o script r em um script bash e recuperar o caminho do script como uma variável bash da seguinte forma:

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

Veja findSourceTraceback() do pacote R.utils , que

Encontra todos os objects ‘srcfile’ gerados por source () em todos os call frames. Isso possibilita descobrir quais arquivos estão atualmente em script por source ().

Eu tive problemas com as implementações acima, pois meu script é operado a partir de um diretório com link simbólico, ou pelo menos é por isso que acho que as soluções acima não funcionaram para mim. Ao longo das linhas de resposta do @ ennuikiller, eu envolvi meu Rscript no bash. Eu defino a variável path usando pwd -P , que resolve as estruturas de diretórios vinculadas por links simbólicos. Em seguida, passe o caminho para o 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) 

Eu usaria uma variante da abordagem do @ steamer25. O ponto é que eu prefiro obter o último script de origem mesmo quando minha session foi iniciada através do Rscript. O snippet a seguir, quando incluído em um arquivo, fornecerá uma variável thisScript contendo o caminho normalizado do script. Eu confesso o (ab) uso de source’ing, então algumas vezes eu invoco o Rscript e o script provido no --file argument sources outro script que origina outro … Algum dia eu vou investir em fazer meu código bagunçado se transformar em um pacote .

 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% dos casos você pode simplesmente usar:

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

Ele não funcionará para chamadas malucas onde o script não é o primeiro argumento, isto é, source(some args, file="myscript") . Use @ hadley nesses casos extravagantes.

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

A abordagem do Steamer25 funciona, mas somente se não houver espaço em branco no caminho. No macOS, pelo menos, o cmdArgs[match] retorna algo como /base/some~+~dir~+~with~+~whitespace/ for /base/some\ dir\ with\ whitespace/ .

Eu trabalhei em torno disso, substituindo o “~ + ~” com um espaço em branco simples antes de devolvê-lo.

 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 você ainda pode estender o bloco else como aprstar fez.

Se, em vez do script, foo.R , saber sua localização de caminho, se você puder alterar seu código para sempre referenciar todos os caminhos de source d de uma root comum, eles podem ser uma grande ajuda:

Dado

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

Isso vai funcionar

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

Veja https://krlmlr.github.io/rprojroot/ para saber como definir as raízes do projeto.

Incrível não existe uma estrutura do tipo ‘$ 0’ em R! Você pode fazer isso com uma chamada system () para um script bash escrito em R:

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

Em seguida, basta dividir o nome scriptpath.sh para other.R

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