同じディレクトリにある別のスクリプトfoo.R
を含むother.R
というスクリプトがあります。
#!/usr/bin/env Rscript
message("Hello")
source("other.R")
しかし、現在の作業ディレクトリに関係なく、R
がother.R
を見つけたいと思います。
つまり、foo.R
は独自のパスを知る必要があります。どうやってやるの?
ここ 問題の簡単な解決策があります。このコマンド:
script.dir <- dirname(sys.frame(1)$ofile)
現在のスクリプトファイルのパスを返します。スクリプトが保存された後に機能します。
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"
これが、デマンが探しているものだと思います。
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))
}
}
frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
PATH <- dirname(frame_files[[length(frame_files)]])
私が忘れてしまったので、それがどのように機能するのか私に尋ねないでください:/
rakensi from Rスクリプトのパスを取得する の答えは、最も正確で本当に素晴らしい私見です。それでも、それはまだダミー機能を組み込んだハックです。他の人が見つけやすいように、ここで引用しています。
sourceDir <-getSrcDirectory(function(dummy){dummy})
これは、ステートメントが配置されたファイルのディレクトリ(ダミー関数が定義されている場所)を提供します。次に、作業ディレクトリを設定し、相対パスを使用するために使用できます。
setwd(sourceDir)
source("other.R")
または絶対パスを作成する
source(paste(sourceDir, "/other.R", sep=""))
これは私のために働く
library(rstudioapi)
rstudioapi::getActiveDocumentContext()$path
Supressingfireの回答のスリム化されたバリアント:
source_local <- function(fname){
argv <- commandArgs(trailingOnly = FALSE)
base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
source(paste(base_dir, fname, sep="/"))
}
私のオールインワン! (--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)
}
}
}
}
}
これは私のために動作します。コマンドライン引数からそれを取り出し、不要なテキストを取り除き、dirnameを実行し、最後にフルパスを取得します:
args <- commandArgs(trailingOnly = F)
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
私はこの質問からほとんどすべてを試しました Rスクリプトのパスを取得 、 現在のパスを取得script 、 現在の.Rファイルの場所を見つける および Rコマンド作業ディレクトリをRstudio のソースファイルの場所に設定したが、最後に手動でCRANテーブルを参照して見つけた
scriptName
ライブラリ
current_filename()
関数を提供します。これは、RStudioでソースを取得するとき、およびRまたはRScript実行可能ファイルを介して呼び出すときに、スクリプトの適切なフルパスを返します。
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))
}
}
}
rprojroot の新しい関数thisfile()
にこの質問に対する答えをまとめて拡張しました。 knitr
を使用した編み物にも使用できます。
私は自分でこれを解決しました。スクリプトの移植性を確保するには、常に次の文字列で開始します。
wd <- setwd(".")
setwd(wd)
「。」が原因で機能します。 Unixコマンド$ PWDのように変換します。この文字列を文字オブジェクトに割り当てると、その文字オブジェクトをsetwd()に挿入することができ、Prestoどのマシンに関係なく、コードは常に現在のディレクトリを作業ディレクトリとして実行されますファイル構造内の場所。 (追加ボーナス:wdオブジェクトをfile.path()(つまり、file.path(wd、 "output_directory")とともに使用して、名前付きディレクトリへのファイルパスに関係なく標準出力ディレクトリを作成できます。この方法では、この方法で参照する前に新しいディレクトリを作成する必要がありますが、それもwdオブジェクトで支援できます。
または、次のコードはまったく同じことを実行します。
wd <- getwd()
setwd(wd)
または、オブジェクトにファイルパスが必要ない場合は、次のようにするだけです。
setwd(".")
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
Getoptパッケージはget_Rscript_filename
関数を提供することに注意してください。この関数は、ここで紹介したものと同じソリューションを使用しますが、標準のRモジュールで既に記述されているため、「get script path 「作成するすべてのスクリプトに機能します。
私はこのアプローチが好きです:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
R.utils パッケージのfindSourceTraceback()
を参照してください。
すべての呼び出しフレームでsource()によって生成されたすべての「srcfile」オブジェクトを検索します。これにより、現在source()によってスクリプト化されているファイルを見つけることができます。
単純に使用できるケースの99%:
sys.calls()[[1]] [[2]]
スクリプトが最初の引数ではないクレイジーな呼び出し、つまりsource(some args, file="myscript")
では機能しません。これらの派手なケースでは@hadleyを使用します。
@ 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)
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")
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ブロックを拡張できます。
#!/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='/'))
スクリプトではなく、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/ を参照してください。
コールスタックを見ると、実行中の各スクリプトのファイルパスを取得できます。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) ))()