Rscript: определить путь к исполняемому скрипту

200

У меня есть script, называемый foo.R, который включает в себя другой script other.R, который находится в том же каталоге:

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

Но я хочу, чтобы R обнаружил, что other.R независимо от текущего рабочего каталога.

Другими словами, foo.R должен знать свой собственный путь. Как я могу это сделать?

  • 1
    Нет. :( Я не видел ни одного решения, которое действительно работает. Помимо обходного пути, чтобы просто передать каталог или использовать переменную окружения.
  • 0
    Я ненавижу полагаться на переменные среды.
Показать ещё 5 комментариев
Теги:
directory
file
path
rscript
r-faq

24 ответа

77

Здесь есть простое решение проблемы. Эта команда:

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

возвращает путь к текущему файлу script. Он работает после сохранения script.

  • 1
    Это просто и отлично работает для меня. Спасибо!
  • 4
    Это не работает для меня. Я запускаю R в Windows. Любая идея?
Показать ещё 14 комментариев
54

Вы можете использовать функцию 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")

вывод:

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"

Это то, что я считаю, что Дехманн ищет.

  • 0
    Что за даунмод?
  • 1
    Я преуменьшил, потому что ваша техника не работает с source как я думал, хотел ОП - но, возможно, я неправильно понял его / ее требование. Но я не могу отключить :( Извините!
Показать ещё 5 комментариев
42

Я не мог заставить приложение 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))
        }
}
  • 3
    Мне это нравится, потому что он работает как с Rscript и с source() в R. Я бы предложил сделать normalizePath() для обеих версий, чтобы в обоих случаях указывать полный путь.
  • 0
    Это единственное, что сработало. Обратите внимание, что для работы library(base) мне потребовалось некоторое время, чтобы понять это, лол
Показать ещё 1 комментарий
33
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, когда я его запускаю.
  • 1
    @Suppressingfire: sys.frames возвращает окружение стека вызовов, поэтому оно действительно имеет смысл только при вызове из функции. Попробуйте, например, foo <- function() {bar <- function() print(sys.frames()); bar()}; foo() . Я не могу понять код @ hadley, потому что в средах нет члена ofile .
Показать ещё 5 комментариев
14

Это работает для меня

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
  • 2
    Я думаю, это работает только изнутри RStudio. При попытке из терминала я получаю сообщение об Error: RStudio not running .
13

Ответ rakensi из Получение пути к R script является наиболее правильным и действительно блестящий ИМХО. Тем не менее, он все еще является взломом, включающим фиктивную функцию. Я цитирую его здесь, чтобы облегчить его поиск другими.

sourceDir < - getSrcDirectory (функция (фиктивный) {фиктивный})

Это дает каталог файла, в который был помещен оператор (где определена фиктивная функция). Затем он может использоваться для установки рабочей директории и использования относительных путей, например.

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

или создать абсолютные пути

 source(paste(sourceDir, "/other.R", sep=""))
  • 1
    Для меня ваше решение было лучшим. Специально потому, что это может быть применено к приложению Shiny, а это по ссылке нет.
  • 1
    Здесь getSrcDirectory - это utils :: getSrcDirectory
Показать ещё 3 комментария
12

Выбранный вариант ответа Supressingfire:

source_local <- function(fname){
    argv <- commandArgs(trailingOnly = FALSE)
    base_dir <- dirname(substring(argv[grep("--file=", argv)], 8))
    source(paste(base_dir, fname, sep="/"))
}
  • 0
    Это не сработало рекурсивно; исходный файл ищет файл данных (но не в том каталоге).
10

Это работает для меня. Просто выгружайте его из аргументов командной строки, удаляет ненужный текст, создает имя dirname и, наконец, получает полный путь от этого:

args <- commandArgs(trailingOnly = F)  
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
  • 0
    Я нашел это самый простой способ, спасибо
  • 0
    Не похоже на работу в RStudio.
9

Мои все в одном! (--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)
                }
            }
        }
    }
}
  • 0
    Не работает с интерактивной сессией R; Я получаю: `` `> source (" csf.R ")> csf () Ошибка: RStudio не работает` ``
6

Я сам это сделал. Чтобы обеспечить переносимость вашего script, всегда начинайте с:

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

Это работает, потому что "." переводится как команда Unix $PWD. Присвоение этой строки символьному объекту позволяет затем вставить этот объект символа в setwd(), а Presto ваш код будет всегда запускаться с его текущим каталогом в качестве рабочего каталога, независимо от того, на чьей машине он находится или где в файловой структуре это располагается. (Дополнительный бонус: wd-объект можно использовать с file.path() (то есть file.path(wd, "output_directory" ), чтобы создать стандартный выходной каталог независимо от пути к файлу, ведущего к вашей именованной директории. Это требует, чтобы вы создали новый каталог, прежде чем ссылаться на него таким образом, но этому тоже может помочь объект wd.

В качестве альтернативы, следующий код выполняет то же самое:

wd <- getwd()
setwd(wd)

или, если вам не нужен путь к файлу в объекте, вы можете просто:

setwd(".")
  • 10
    Нету. Это находит каталог процесса, а не сам файл.
  • 0
    Это работало для меня в Windows с RStudio в интерактивном режиме.
6

Мне понравилось решение 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))
        }
    }
}
  • 0
    Исходный код в Rstudio дал мне возможность, но debugSource дал имя файла, так что ваше решение работает хорошо, но комментарии к коду не совсем верны в моем случае.
  • 0
    Это работает для меня, когда я выполняю в CMD.
5

Я обернул и расширил ответы на этот вопрос в новую функцию thisfile() в rprojroot. Также работает для вязания knitr.

3

Я попробовал почти все из этого вопроса: " Получить путь к R-сценарию", " Получить путь к текущему сценарию", " Найти местоположение текущего .R файла" и " Команда R" для установки рабочего каталога на местоположение исходного файла в Rstudio, но в конце я оказался вручную просматривая таблицу CRAN и находя

библиотека scriptName

которая предоставляет функцию current_filename(), которая возвращает правильный полный путь скрипта при поиске в RStudio, а также при вызове через исполняемый файл R или RScript.

2

Обратите внимание, что пакет getopt предоставляет функцию get_Rscript_filename, которая просто использует то же самое решение, представленное здесь, но уже написано для вас в стандартном R-модуле, поэтому вам не нужно копировать и вставлять "get script path" в каждый script, который вы пишете.

  • 0
    Он всегда возвращает NA, даже если я создаю сценарий, который печатает его вывод, а затем вызываю сценарий, например, с помощью R -e "library(getopt); testscript.R"
  • 1
    Как следует из названия функции, вам нужно запустить ваш скрипт, используя Rscript .
Показать ещё 1 комментарий
2

Мне нравится этот подход:

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

Вы можете обернуть 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
  • 2
    Это требует, чтобы у вас был путь к сценарию. Он не позволяет вам создать действительно переносимый R-скрипт, который может запускаться где угодно.
  • 0
    @ EtienneLow-Décarie Не требует пути к скрипту, он получает его из bash. Основная проблема заключается в том, что это не надежный способ получить путь. Нечто подобное предпочтительнее, как в stackoverflow.com/questions/59895/… path_to_script = "$ (cd" $ (dirname "$ {BASH_SOURCE [0]}") "&& pwd)"
1

99% случаев, которые вы могли бы просто использовать:

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

Он не будет работать для сумасшедших вызовов, где script не является первым аргументом, т.е. source(some args, file="myscript"). Используйте @hadley в этих причудливых случаях.

  • 0
    Не изнутри RStudio, хотя, кроме как при поиске
1

Я бы использовал вариант подхода @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))
  }
})()
1

У меня были проблемы с реализациями, описанными выше, поскольку мой 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)
1

Смотрите findSourceTraceback() пакета R.utils, который

Находит все объекты srcfile, сгенерированные источником() во всех кадрах вызовов. Это позволяет узнать, какие файлы в настоящее время написаны с помощью source().

0

Удивительно, что в 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")
0

Если вместо сценария foo.R, зная его местоположение, вы можете изменить код так, чтобы он всегда ссылался на все source пути из общего root то это может быть очень полезно:

Дано

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

Это будет работать

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

См. Https://krlmlr.github.io/rprojroot/, чтобы узнать, как определить корни проекта.

0

Подход 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.

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='/'))
  • 0
    Я все еще получаю ошибку «Ошибка в sys.frame (1): не так много кадров в стеке»

Ещё вопросы

Сообщество Overcoder
Наверх
Меню