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


255

私はと呼ばれるスクリプト持っているfoo.R別のスクリプトが含まother.R同じディレクトリにあります、:

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

しかし、私は現在の作業ディレクトリが何であっRother.Rもそれを見つけたいです。

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


2
いいえ。(実際に機能する解決策はありません。ディレクトリを渡すか、環境変数を使用するという回避策は別として
Frank

3
これは、スクリプトを完全に移植可能にし、Rの新人でも実行できるようにするのはすばらしいことです。
エティエンヌローデカリ

4
すべての回答で、ある時点でパスを入力する必要があるようです(少なくともファイルのソースとして)。誰かに圧縮フォルダーを送信し、そのフォルダー内でRスクリプトファイルを実行すると、そのフォルダーから読み取られ、そのフォルダーに保存されると便利です。
エティエンヌローデカリー

10
この単一の問題が実際に私が完全にPythonに移行できる理由になる可能性があります
Giacomo

5
@giac_manが、私はRが全てで仕事に非常に困難にまで追加このような小さな問題の数百に満ちている感じ。
マイケル・バートン

回答:


102

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

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

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


4
私にはうまくいきません。WindowsでRを実行しています。何か案が?
Ehsan88 2014

4
保存されたスクリプトを使用して同じエラーが発生し、新しくインストールしてWindowsでR 3.2.0を実行します...
RalfB

27
このエラーは、dirname(sys.frame(1)$ofile)Rstudioから直接実行しようとしたときに発生します。source( "other.R")を使用してスクリプトが実行され、dirname(sys.frame(1)$ofile)内部にある場合は問題なく動作し"other.R"ます。
Murta、2015

4
rscript.exeを使用してスクリプトとして呼び出すとき、つまりsource()を使用しないと、「スタックにそれほど多くのフレームがない」エラーが発生しました。そのため、代わりに以下のSuppressingfireのソリューションを使用する必要がありました
Mark Adamson

3
NULLこれをサーバーに配置するとゲル化します。Rを使用すると、光沢が出ます
Paul

75

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)

その他.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"

これは、dehmannが探しているものだと思います。


ダウンモッドとは何ですか?
Suppressingfire、

2
あなたのテクニックがsourceOPが望んでいたように機能しないので、私はダウンモッド化しました-しかし、おそらく彼/彼女の要件を誤って読みました。しかし、私はダウンモードを解除することはできません:(すみません!
ハドリー2009年

しかし、実際には、ソースでうまく機能します!source(other.name)だけで正しく動作します。
Suppressingfire、

3
パスの連結については、使用する方が良いother.name <- file.path(script.basename, "other.R")
Jason

1
commandArgs(trailingOnly = FALSE)光沢のあるアプリケーションでserver.R内で実行しようとすると、が表示されます[1] "RStudio" "--interactive"。呼び出し元のディレクトリに関する情報はありません。
Paul

57

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

6
それは両方で動作するので、私はこれが好きRscriptsource()R.私がやってお勧めしたいの中normalizePath()には、両方のケースで完全なパスを与えるように、両方のバージョンに。
2014年

1
これが機能した唯一のものです。注、これが機能するためにlibrary(base)は、それを理解するのに少し時間がかかりました
O.rka

2
これは私のために働いた解決策なので、あなたは私の票を得ます
ヴィンスW.

1
これが誰かに役立つ場合、元の投稿は、で意味さsource(file.path(dirname(thisFile()), "other.R"))foo.Rます。これは私にとってはうまくいきます。
キム

1つの問題。RStudioで、どれを呼び出すかmain.Rをソース化するhelper.RとしますthisFile()main.Rではなくのパスをフェッチしますhelper.R。ここで何かヒントはありますか?
Wassadamo

37
frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])

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


2
それはどのような状況で機能しますか?print(sys.frames())を実行するとNULLになります。
Suppressingfire

1
@Suppressingfire:sys.frames呼び出しスタックの環境を返すため、関数から呼び出された場合にのみ意味があります。たとえば、を試してくださいfoo <- function() {bar <- function() print(sys.frames()); bar()}; foo()。環境にofileメンバーがいないため、@ hadleyのコードを理解できません。
リッチーコットン

1
ファイルを入手する必要があります-つまり、そのコードを保存してから実行する場合 source("~/code/test.r")PATHに設定され~/desktopます。最上位で評価すると、NULLが返されます。
ハドリー2009年

4
これは私の質問の答えにはなりません。「other.R」ファイルを自動的に見つける必要があります。x$ofileは未定義なのでframe_files、空です。
フランク

@hadley、非常に便利なコード。スクリプトがアクティブに開発されているときに、ほとんどすべてのスクリプトに追加する「現在のスクリプトの再読み込み」ユーティリティ機能を一般化することができました。RScriptリローダー
Sim

29

これは私のために働く

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

4
これは、RStudio内からのみ機能すると思います。ターミナルから試してみますError: RStudio not running
イスタ

より具体的には、RスタジオでRスクリプトから実行すると機能します。RStudioのコンソールでも""、私の場合は正しい結果が得られません
ケイ

これは、フォーカスされているドキュメントを変更しない限り、Rstudioでインタラクティブに実行中に機能します。実行する行を送信し、実行中に別のドキュメントに切り替えると、他のドキュメントへのパスが返されます。
パトリック

26

ラーケンシーの答えRスクリプトのパスの取得からは、最も正確で本当に素晴らしいIMHOです。それでも、それはまだダミー関数を組み込んだハックです。他の人が見つけやすくするために、ここでは引用しています。

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

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

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

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

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

1
私にとって、あなたの解決策は最高でした。それは特に、光沢のあるアプリに適用でき、リンクされていないアプリに適用できるためです。
jcarlos

1
ここで、getSrcDirectoryはutils :: getSrcDirectoryです
RubenLaguna

5
これはLinux / Macではうまく機能するかもしれませんが、WindowsのインタラクティブRStudioセッションではうまくいきませんでした。sourceDir空白だった。
Contango 2017

1
対話型端末の@Contango、パスはありません!!! ファイルへのパスが必要です。
pommedeterresautee

1
私は取得していますcharacter(0)。提案?
abalter

16

私のオールインワン!(2019年1月1日、RStudioコンソールに対応するように更新)

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

インタラクティブRセッションでは機能しません。取得しています: `` `> source(" csf.R ")> csf()Error:RStudio not
running`

これは素晴らしい。誰かがパッケージを作れますか?
ジョーフラック

これは、フォーカスされているドキュメントを変更しない限り、Rstudioでインタラクティブに実行中に機能します。実行する行を送信し、実行中に別のドキュメントに切り替えると、他のドキュメントへのパスが返されます。
パトリック

13

Supressingfireの回答のスリム化されたバリアント:

source_local <- function(fname){
    argv <- commandArgs(trailingOnly = FALSE)
    base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
    source(paste(base_dir, fname, sep="/"))
}

これは再帰的に機能しませんでした。ソースファイルがデータファイルを探します(ただし、間違ったディレクトリにあります)。
Unfun Cat

11

これは私にとってはうまくいきます。コマンドライン引数からそれを把握し、不要なテキストを取り除き、dirnameを実行して、最後に完全パスを取得します。

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


6

私の目的にとって最も堅牢なように見えるので、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))
        }
    }
}

Rstudioのソースは私にforileを与えましたが、debugSourceはfileNameを与えたので、ソリューションはうまく機能しますが、コードのコメントは私の場合は完全に正しくありません
Mark Adamson

6

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

scriptName 図書館

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


2
Package ‘scriptName’ was removed from the CRAN repository.-今何?:o
Bojan P.

3

私にもこの問題があり、上記の解決策はどれもうまくいきませんでした。多分sourceそのようなもので、しかしそれは十分に明確ではありませんでした。

私はこれをエレガントな解決策として見つけました:

paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE),"/")

その中で重要なのはfileSnapshot()、ファイルに関する多くの情報を提供することです。8つの要素のリストを返します。pathリスト要素として選択すると、パスは\\セパレータとして返されるので、コードの残りの部分はそれを変更するだけです。

これがお役に立てば幸いです。


1
これは、Linuxマシンでは機能しませんでした。ファイルのパスを返す代わりに、現在のディレクトリが返されました。次の1行のコードでTEST.Rというテストスクリプトを作成しました:print(fileSnapshot()$ path)これを次のフォルダーに保存しました:/ opt / home / boops / Desktop / Testfolder / TEST.RI次に、デスクトップに移動し、ファイルを実行しようとしました:boops @ linuxserver:〜/ Desktop $ Rscript /opt/home/boops/Desktop/Testfolder/TEST.R [1 ] "/ opt / home / boops / Desktop"
Boops Boops

私もうまくいきませんでした。「here」ライブラリを使用すると、「here()」と同じものを返します。現在開いているRプロジェクトへのパスが返されましたが、実行されているファイルそのものではありませんでした。
Joe Flack

2

次のように、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

3
これには、スクリプトパスが必要です。どこからでも実行できる真に移植可能なRスクリプトを作成することはできません。
エティエンヌローデカリ

@EtienneLow-Décarieスクリプトパスを必要とせず、bashから取得します。主な問題は、パスを取得するための信頼できる方法ではないということです。stackoverflow.com/questions/59895/…のように、このようなものが推奨されます 。path_to_script= "$(cd" $(dirname "$ {BASH_SOURCE [0]}") "&& pwd)"
John Haberstroh

2

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

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

2

私はこれを自分で解決しました。スクリプトの移植性を確保するために、常に次のことから始めてください。

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

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

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

wd <- getwd()
setwd(wd)

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

setwd(".")

11
いいえ。これは、ファイル自体ではなく、プロセスのディレクトリを検索します。
user1071847 16

これはWindowsでインタラクティブモードのRStudioを使用して動作しました。
Contango 2017

2

getoptパッケージが提供するget_Rscript_filename関数は、ここで示したものと同じソリューションを使用しますが、標準のRモジュールですでに作成されているため、「スクリプトパスの取得」関数をすべてのスクリプトにコピーして貼り付ける必要はありません。あなたが書く。


それは常に私がその出力を出力するスクリプトを作成する場合でも、NAを返し、その後でスクリプトの例を呼び出すR -e "library(getopt); testscript.R"
bokov

1
関数の名前が示すように、を使用してスクリプトを実行する必要がありますRscript
ライアンC.トンプソン

ああ、おっと。ありがとう。
bokov

1

参照findSourceTraceback()R.utilsの、パッケージました

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


1

スクリプトがシンボリックリンクディレクトリから操作されているため、上記の実装に問題がありました。少なくとも、上記の解決策がうまくいかなかったのはそのためです。@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

@ steamer25のアプローチのバリアントを使用します。ポイントは、セッションがRscriptを介して開始された場合でも、最後のソーススクリプトを取得したいということです。次のスニペットをファイルに含めるthisScriptと、スクリプトの正規化されたパスを含む変数が提供されます。私はソースの(乱用)使用を告白します。そのため、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))
  }
})()

1

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

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

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


ただし、調達時以外はRStudio内からではありません
nJGL

1

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ブロックを拡張することもできます。


1

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

与えられた

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

これは動作します

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

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


私にとって、ここのパッケージはまさに仕事をし、簡単な解決策のようです
Ron

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

それでもエラー「sys.frame(1)のエラー:スタックにそれほど多くのフレームはありません」が表示される
Michael Barton

0

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)

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

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

エラーメッセージが表示されるreadLink: illegal option -- e usage: readLink [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
altabq

0

コールスタックを確認することで、実行中の各スクリプトのファイルパスを取得できます。最も有用な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) ))()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.