Ответ 1
Здесь есть простое решение проблемы. Эта команда:
script.dir <- dirname(sys.frame(1)$ofile)
возвращает путь к текущему файлу script. Он работает после сохранения script.
У меня есть script, называемый foo.R
, который включает в себя другой script other.R
, который находится в том же каталоге:
#!/usr/bin/env Rscript
print("Hello")
source("other.R")
Но я хочу, чтобы R
обнаружил, что other.R
независимо от текущего рабочего каталога.
Другими словами, foo.R
должен знать свой собственный путь. Как я могу это сделать?
Здесь есть простое решение проблемы. Эта команда:
script.dir <- dirname(sys.frame(1)$ofile)
возвращает путь к текущему файлу script. Он работает после сохранения script.
Вы можете использовать функцию commandArgs
чтобы получить все параметры, которые были переданы Rscript фактическому интерпретатору R, и найти их для --file=
. Если ваш скрипт был запущен с пути или если он был запущен с полным путем, имя script.name
ниже будет начинаться с '/'
. В противном случае он должен быть относительно cwd
и вы можете объединить два пути, чтобы получить полный путь.
Изменить: звучит так, как будто вам нужен только script.name
выше и чтобы удалить последний компонент пути. Я удалил ненужный cwd()
очистил основной скрипт и опубликовал мой other.R
. Просто сохраните этот сценарий и 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)
другое.Р:
print("hello")
вывод:
[email protected]:~$ main.R
[1] "Sourcing /home/burner/bin/other.R from /home/burner/bin/main.R"
[1] "hello"
[email protected]:~$ bin/main.R
[1] "Sourcing bin/other.R from bin/main.R"
[1] "hello"
[email protected]:~$ cd bin
[email protected]:~/bin$ main.R
[1] "Sourcing ./other.R from ./main.R"
[1] "hello"
Это то, что я считаю, что Дехманн ищет.
Я не мог заставить приложение Suppressingfire работать, когда "источник" находится на консоли R.
Я не мог заставить решение hasley работать при использовании 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 из Получение пути к R script является наиболее правильным и действительно блестящий ИМХО. Тем не менее, он все еще является взломом, включающим фиктивную функцию. Я цитирую его здесь, чтобы облегчить его поиск другими.
sourceDir < - getSrcDirectory (функция (фиктивный) {фиктивный})
Это дает каталог файла, в который был помещен оператор (где определена фиктивная функция). Затем он может использоваться для установки рабочей директории и использования относительных путей, например.
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)
#' 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)])))
Я обернул и расширил ответы на этот вопрос в новую функцию thisfile()
в rprojroot. Также работает для вязания knitr
.
Мне понравилось решение steamer25, поскольку оно кажется самым надежным для моих целей. Однако при отладке в RStudio (в окнах) путь не будет установлен правильно. Причина заключается в том, что если в RStudio установлена точка останова, для поиска файла используется альтернативная команда "отладочный источник", которая устанавливает путь script несколько иначе. Вот окончательная версия, которую я сейчас использую, которая учитывает это альтернативное поведение в 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))
}
}
}
Я попробовал почти все из этого вопроса: получение пути к R-сценарию, получение пути к текущему сценарию, поиск местоположения текущего файла .R и команда R для установки рабочего каталога на местоположение исходного файла в Rstudio, но в конце я оказался вручную просматривая таблицу CRAN и находя
библиотека scriptName
которая предоставляет функцию current_filename()
, которая возвращает правильный полный путь скрипта при поиске в RStudio, а также при вызове через исполняемый файл R или RScript.
Я сам это сделал. Чтобы обеспечить переносимость вашего script, всегда начинайте с:
wd <- setwd(".")
setwd(wd)
Это работает, потому что "." переводится как команда Unix $PWD. Присвоение этой строки символьному объекту позволяет затем вставить этот объект символа в setwd(), а Presto ваш код будет всегда запускаться с его текущим каталогом в качестве рабочего каталога, независимо от того, на чьей машине он находится или где в файловой структуре это располагается. (Дополнительный бонус: wd-объект можно использовать с file.path() (то есть file.path(wd, "output_directory" ), чтобы создать стандартный выходной каталог независимо от пути к файлу, ведущего к вашей именованной директории. Это требует, чтобы вы создали новый каталог, прежде чем ссылаться на него таким образом, но этому тоже может помочь объект wd.
В качестве альтернативы, следующий код выполняет то же самое:
wd <- getwd()
setwd(wd)
или, если вам не нужен путь к файлу в объекте, вы можете просто:
setwd(".")
Вы можете обернуть r script в bash script и получить путь script как переменную bash следующим образом:
#!/bin/bash
# [environment variables can be set here]
path_to_script=$(dirname $0)
R --slave<<EOF
source("$path_to_script/other.R")
EOF
Мне нравится этот подход:
this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
Обратите внимание, что пакет getopt предоставляет функцию get_Rscript_filename
, которая просто использует то же самое решение, представленное здесь, но уже написано для вас в стандартном R-модуле, поэтому вам не нужно копировать и вставлять "get script path" в каждый script, который вы пишете.
Смотрите findSourceTraceback()
пакета R.utils, который
Находит все объекты srcfile, сгенерированные источником() во всех кадрах вызовов. Это позволяет узнать, какие файлы в настоящее время написаны с помощью source().
У меня были проблемы с реализациями, описанными выше, поскольку мой script работает из символического каталога, или, по крайней мере, почему я думаю, что вышеупомянутые решения не сработали для меня. В ответ на вопрос @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)
Я бы использовал вариант подхода @steamer25. Дело в том, что я предпочитаю получать последний источник script, даже когда мой сеанс был запущен через Rscript. Следующий фрагмент, включенный в файл, предоставит переменную thisScript
, содержащую нормализованный путь script.
Я признаю (ab) использование source'ing, поэтому иногда я вызываю Rscript и script, предоставленные в источнике аргументов --file
, другой script, который источает другой... Когда-нибудь я буду инвестировать в создание моего грязного кода превращается в пакет.
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% случаев, которые вы могли бы просто использовать:
sys.calls()[[1]] [[2]]
Он не будет работать для сумасшедших вызовов, где script не является первым аргументом, т.е. source(some args, file="myscript")
. Используйте @hadley в этих причудливых случаях.
#!/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 работает, но только если в пути нет пробелов. На 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))
}
}
Очевидно, вы все равно можете расширить блок else, как это сделал aprstar.
Если вместо сценария foo.R
, зная его местоположение, вы можете изменить свой код так, чтобы он всегда ссылался на все source
пути из общего root
то это может быть очень полезно:
Дано
/app/deeply/nested/foo.R
/app/other.R
Это будет работать
#!/usr/bin/env Rscript
library(here)
source(here("other.R"))
См. Https://rprojroot.r-lib.org/, чтобы узнать, как определить корни проекта.
Удивительно, что в R нет структуры типа '$ 0'! Вы можете сделать это с помощью вызова system() скрипта bash, написанного на R:
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")