web-dev-qa-db-ja.com

実行中のスクリプトのパスを決定する

同じディレクトリにある別のスクリプトfoo.Rを含むother.Rというスクリプトがあります。

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

しかし、現在の作業ディレクトリに関係なく、Rother.Rを見つけたいと思います。

つまり、foo.Rは独自のパスを知る必要があります。どうやってやるの?

226
Frank

ここ 問題の簡単な解決策があります。このコマンド:

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

現在のスクリプトファイルのパスを返します。スクリプトが保存された後に機能します。

90

commandArgs関数を使用して、Rscriptから実際のRインタープリターに渡されたすべてのオプションを取得し、--file=を検索できます。スクリプトがパスから起動された場合、またはフルパスで起動された場合、以下のscript.name'/'で始まります。それ以外の場合は、cwdに相対的である必要があり、2つのパスを連結して完全なパスを取得できます。

編集: 上記のscript.nameのみが必要で、パスの最終コンポーネントを取り除く必要があるように思えます。不要なcwd()サンプルを削除し、メインスクリプトをクリーンアップして、other.Rを投稿しました。このスクリプトとother.Rスクリプトを同じディレクトリに保存し、chmod +xして、メインスクリプトを実行するだけです。

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

出力

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"

これが、デマンが探しているものだと思います。

64
Suppressingfire

Rコンソールから「ソース」を指定すると、Suppressingfireのソリューションが機能しませんでした。
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))
        }
}
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)]])

私が忘れてしまったので、それがどのように機能するのか私に尋ねないでください:/

35
hadley

rakensi from Rスクリプトのパスを取得する の答えは、最も正確で本当に素晴らしい私見です。それでも、それはまだダミー機能を組み込んだハックです。他の人が見つけやすいように、ここで引用しています。

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

これは、ステートメントが配置されたファイルのディレクトリ(ダミー関数が定義されている場所)を提供します。次に、作業ディレクトリを設定し、相対パスを使用するために使用できます。

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

または絶対パスを作成する

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

これは私のために働く

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

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

私のオールインワン! (--01/09/2019 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

これは私のために動作します。コマンドライン引数からそれを取り出し、不要なテキストを取り除き、dirnameを実行し、最後にフルパスを取得します:

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

私はこの質問からほとんどすべてを試しました Rスクリプトのパスを取得現在のパスを取得script現在の.Rファイルの場所を見つける および Rコマンド作業ディレクトリをRstudio のソースファイルの場所に設定したが、最後に手動でCRANテーブルを参照して見つけた

scriptName ライブラリ

current_filename()関数を提供します。これは、RStudioでソースを取得するとき、およびRまたはRScript実行可能ファイルを介して呼び出すときに、スクリプトの適切なフルパスを返します。

6
Bojan P.

Steamer25のソリューションは、私の目的にとって最も堅牢であると思われるので気に入っています。ただし、RStudio(Windows)でデバッグする場合、パスは正しく設定されません。理由は、RStudioでブレークポイントが設定されている場合、ファイルのソースは、スクリプトパスを少し異なるように設定する代替「デバッグソース」コマンドを使用するためです。以下は、デバッグ時にRStudio内でこの代替動作を説明する現在使用している最終バージョンです。

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

rprojroot の新しい関数thisfile()にこの質問に対する答えをまとめて拡張しました。 knitrを使用した編み物にも使用できます。

6
krlmlr

私は自分でこれを解決しました。スクリプトの移植性を確保するには、常に次の文字列で開始します。

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

「。」が原因で機能します。 Unixコマンド$ PWDのように変換します。この文字列を文字オブジェクトに割り当てると、その文字オブジェクトをsetwd()に挿入することができ、Prestoどのマシンに関係なく、コードは常に現在のディレクトリを作業ディレクトリとして実行されますファイル構造内の場所。 (追加ボーナス:wdオブジェクトをfile.path()(つまり、file.path(wd、 "output_directory")とともに使用して、名前付きディレクトリへのファイルパスに関係なく標準出力ディレクトリを作成できます。この方法では、この方法で参照する前に新しいディレクトリを作成する必要がありますが、それもwdオブジェクトで支援できます。

または、次のコードはまったく同じことを実行します。

wd <- getwd()
setwd(wd)

または、オブジェクトにファイルパスが必要ない場合は、次のようにするだけです。

setwd(".")
4

Rスクリプトをbashスクリプトでラップし、次のようにスクリプトのパスをbash変数として取得できます。

#!/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

Getoptパッケージはget_Rscript_filename関数を提供することに注意してください。この関数は、ここで紹介したものと同じソリューションを使用しますが、標準のRモジュールで既に記述されているため、「get script path 「作成するすべてのスクリプトに機能します。

2
Ryan Thompson

私はこのアプローチが好きです:

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

R.utils パッケージのfindSourceTraceback()を参照してください。

すべての呼び出しフレームでsource()によって生成されたすべての「srcfile」オブジェクトを検索します。これにより、現在source()によってスクリプト化されているファイルを見つけることができます。

1
HenrikB

単純に使用できるケースの99%:

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

スクリプトが最初の引数ではないクレイジーな呼び出し、つまりsource(some args, file="myscript")では機能しません。これらの派手なケースでは@hadleyを使用します。

1
antonio

@ steamer25のアプローチのバリアントを使用します。要点は、セッションがRscriptを介して開始された場合でも、最後のソーススクリプトを取得することです。次のスニペットは、ファイルに含まれる場合、スクリプトの正規化されたパスを含む変数thisScriptを提供します。ソース化の(ab)使用を告白するため、Rscriptを呼び出し、--file引数で提供されるスクリプトが別のスクリプトをソースとする別のスクリプトをソースする場合があります...いつかは厄介なコードをパッケージ。

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

スクリプトがシンボリックリンクされたディレクトリから操作されるため、上記の実装に問題がありました。または、少なくともそれが、上記のソリューションが私にとってうまくいかなかったと思う理由です。 @ennuikillerの答えに沿って、私はRscriptをbashでラップしました。シンボリックリンクされたディレクトリ構造を解決するpwd -Pを使用してパス変数を設定します。次に、パスを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

Rには「$ 0」タイプの構造はありません! Rで記述されたbashスクリプトのsystem()呼び出しでそれを行うことができます。

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

次に、other.Rのscriptpath.sh名を分割します

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

Steamer25のアプローチは機能しますが、パスに空白がない場合のみです。 macOSでは、少なくともcmdArgs[match]/base/some~+~dir~+~with~+~whitespace/に対して/base/some\ dir\ with\ whitespace/のようなものを返します。

これを回避するには、「〜+〜」を返す前に単純な空白に置き換えます。

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

明らかに、aprstarのようにelseブロックを拡張できます。

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

スクリプトではなく、foo.R、そのパスの場所を知っている場合、共通のsourceからすべてのroot 'dパスを常に参照するようにコードを変更できる場合、これらは非常に役立ちます。 :

与えられた

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

これは動作します

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

プロジェクトのルートを定義する方法については、 https://rprojroot.r-lib.org/ を参照してください。

0
mmell

コールスタックを見ると、実行中の各スクリプトのファイルパスを取得できます。2つの最も有用なものは、おそらく現在実行中のスクリプトか、ソースとなる最初のスクリプト(エントリ)です。

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