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

255

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

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

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

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

Фрэнк
источник
2
Нет .( Я не видел ни одного решения, которое действительно работает. Помимо обходного пути, чтобы просто передать каталог или использовать переменную среды.
Frank
3
Было бы удивительно сделать скрипты полностью переносимыми и исполняемыми даже R neofites!
Этьен Лоу-Декари
4
Похоже, что все ответы требуют, чтобы вы указали путь в какой-то момент (по крайней мере, для получения файла)! Было бы замечательно, если бы вы могли отправить кому-либо сжатую папку, и запуск любого файла сценария R в этой папке считывал и сохранял в этой папке.
Этьен Лоу-Декари
10
эта единственная проблема могла фактически стать причиной, почему я мог полностью перейти на Python
Giacomo
5
@giac_man, я чувствую, что R полон сотен крошечных проблем, подобных этой, которые в
Майкл Бартон,

Ответы:

102

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

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

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

this.is.not.a.nick
источник
4
Это не работает для меня. Я запускаю R в Windows. Любая идея?
Ehsan88
4
Получил ту же ошибку, с сохраненным скриптом и недавно установленным и запустил R 3.2.0 на Windows ...
RalfB
27
Эта ошибка возникает при попытке выполнить dirname(sys.frame(1)$ofile)напрямую из Rstudio. Он работает нормально, когда скрипт выполняется с использованием источника ("other.R"), и dirname(sys.frame(1)$ofile)находится внутри "other.R".
Мурта
4
Я получил сообщение об ошибке «не так много кадров в стеке» при вызове скрипта с помощью rscript.exe, т.е. без использования source (). поэтому мне пришлось вместо этого использовать решение из «Подавления огня» ниже
Марк Адамсон,
3
Я гел, NULLкогда это помещено в server.R при использовании блестящего
Пол
75

Вы можете использовать commandArgsфункцию, чтобы получить все опции, которые были переданы Rscript фактическому интерпретатору R, и найти их --file=. Если ваш скрипт был запущен с пути или если он был запущен с полным путем, то script.nameнижеприведенное будет начинаться с '/'. В противном случае оно должно быть относительноcwd и вы можете объединить два пути, чтобы получить полный путь.

Редактировать: звучит так, как будто вам нужно только 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)

другое.Р :

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"

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

Suppressingfire
источник
Что за даунмод?
Подавление
2
Я преуменьшил, потому что ваша техника не работает так, sourceкак я думал, хотел ОП - но, возможно, я неправильно понял его / ее требование. Но я не могу un-downmod :( Извините!
Хэдли
Но на самом деле, он отлично работает с источником! Просто источник (other.name), и он работает правильно.
Подавление
3
Для конкатенации путей лучше использоватьother.name <- file.path(script.basename, "other.R")
Jason
1
Когда я пытаюсь запустить commandArgs(trailingOnly = FALSE)внутри server.R в блестящем приложении, я получаю [1] "RStudio" "--interactive". Нет информации о каталоге, из которого он был вызван.
Пол
57

Я не мог заставить решение Suppressingfire работать при «исходном» режиме с консоли R.
Я не мог заставить решение Хадли работать при использовании 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))
        }
}
steamer25
источник
6
Мне это нравится, потому что он работает с обоими Rscriptи source()внутри R. Я бы предложил сделать normalizePath()на обеих версиях, так что он дает полный путь в обоих случаях.
WCH
1
Это единственное, что сработало. Обратите внимание, что для того, чтобы это сработало, library(base)мне потребовалось некоторое время, чтобы понять это.
Lol
2
Вы, сэр, получите мой голос, потому что это решение, которое сработало для меня
Винс В.
1
Если это кому-нибудь поможет, для оригинального поста это будет означать source(file.path(dirname(thisFile()), "other.R"))в foo.R. Это работает для меня.
Ким,
Одна проблема Предположим, что в RStudio I источник, main.Rкакой источник, helper.Rкоторый вызывает thisFile(). Он выберет путь main.Rвместо helper.R. Любые советы здесь?
Вассадамо
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)]])

Не спрашивайте меня, как это работает, потому что я забыл: /

Hadley
источник
2
В каком контексте это работает? print (sys.frames ()) имеет значение NULL, когда я его запускаю.
Подавление
1
@Suppressingfire: sys.framesвозвращает окружение стека вызовов, поэтому оно действительно имеет смысл только при вызове из функции. Попробуйте, например foo <- function() {bar <- function() print(sys.frames()); bar()}; foo(). Я не могу понять код @ hadley, хотя, потому что окружение не имеет ofileчлена.
Ричи Коттон
1
Вы должны получить исходный файл - т.е. если я сохраню этот код, то запустим source("~/code/test.r"), PATHбудет установлено значение ~/desktop. Если вы просто оцените его на верхнем уровне, он вернет NULL.
Хэдли
4
Это не отвечает на мой вопрос. Мне нужно автоматически найти файл "other.R". x$ofileне определено, поэтому frame_filesпусто.
Франк
@ Хэдли, очень полезный код. Мне удалось обобщить служебную функцию «перезагрузить текущий сценарий», которую я добавляю почти во все сценарии, когда они находятся в активной разработке. Перезагружатель RScript
Sim
29

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

library(rstudioapi)    
rstudioapi::getActiveDocumentContext()$path
ColinTea
источник
4
Я думаю, это работает только изнутри RStudio. Пробую из терминала получаю Error: RStudio not running.
Иста
более конкретно это работает, если запустить из R-скрипта в R-студии. Даже на консоли в RStudio это не даст правильного результата ""в моем случае
Кей
Это работает при интерактивной работе в Rstudio, если вы не измените фокус документа . Если вы отправите строки для запуска, а затем переключитесь на другой документ во время их выполнения, будет возвращен путь к другому документу.
Патрик
26

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

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

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

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

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

 source(paste(sourceDir, "/other.R", sep=""))
cuffel
источник
1
Для меня ваше решение было лучшим. Тем более, что его можно применить к блестящему приложению, а это по ссылке - нет.
jcarlos
1
Здесь getSrcDirectory - это utils :: getSrcDirectory
RubenLaguna
5
Это может хорошо работать в Linux / Mac, но у меня не получилось во время интерактивного сеанса RStudio под Windows. sourceDirбыло пустым
контанго
1
@Contango на интерактивном терминале, нет пути !!! Вы хотите путь к файлу.
pommedeterresautee
1
Я получаю character(0). Предложения?
19
16

Мои все в одном! (- 01/01/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)
                }
            }
        }
    }
}
Джерри Т
источник
Не работает с интерактивной сессией R; Я получаю: `` `> source (" csf.R ")> csf () Ошибка: RStudio не работает` ``
ManicMailman
Это круто. Может кто-нибудь сделать пакет?
Джо Флэк
Это работает при интерактивной работе в 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="/"))
}
momeara
источник
Это не сработало рекурсивно; исходный файл ищет файл данных (но не в том каталоге).
Несчастный кот
11

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

args <- commandArgs(trailingOnly = F)  
scriptPath <- normalizePath(dirname(sub("^--file=", "", args[grep("^--file=", args)])))
Eddi
источник
8

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

krlmlr
источник
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))
        }
    }
}
aprstar
источник
Исходный код в Rstudio дал мне возможность, но debugSource дал fileName, поэтому ваше решение работает хорошо, но комментарии к коду не совсем верны в моем случае
Марк Адамсон,
6

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

scriptName библиотека

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

Боян П.
источник
2
Package ‘scriptName’ was removed from the CRAN repository.- что теперь? : o
Боян П.
3

У меня тоже была эта проблема, и ни одно из вышеперечисленных решений не помогло мне. Может быть сsource или тому подобное, но это было недостаточно ясно.

Я нашел это, для меня элегантное, решение:

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

Важно то, что fileSnapshot()это дает вам много информации о файле. Возвращает список из 8 элементов. Когда вы выбираете pathв качестве элемента списка, путь возвращается с\\ разделителем, так что остальная часть кода просто изменить это.

Надеюсь, это поможет.

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

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

this.file <- sys.frame(tail(grep('source',sys.calls()),n=1))$ofile
this.dir <- dirname(this.file)
kuna.matata
источник
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
Это работало для меня в Windows с RStudio в интерактивном режиме.
контанго
2

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

Райан К. Томпсон
источник
Он всегда возвращает NA, даже если я создаю сценарий, который печатает его вывод, а затем вызываю сценарий, например, с помощьюR -e "library(getopt); testscript.R"
bokov
1
Как видно из названия функции, вам нужно запустить ваш скрипт используя Rscript.
Райан Томпсон
Ах, ой. Спасибо.
Боков
1

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

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

HenrikB
источник
1

У меня были проблемы с реализациями, описанными выше, так как мой скрипт работает из каталога с символическими ссылками, или, по крайней мере, поэтому я думаю, что вышеупомянутые решения не сработали для меня. Следуя ответу @ ennuikiller, я завернул свой Rscript в bash. Я установил переменную path с помощью 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содержащую нормализованный путь сценария. Я признаю (ab) использование source'ing, поэтому иногда я вызываю 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's в этих необычных случаях.

антонио
источник
Не изнутри 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))
  }
}

Очевидно, вы можете расширить блок else, как это сделал aprstar.

iball
источник
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/, чтобы узнать, как определить корни проекта.

mmell
источник
Для меня пакет «здесь» делает именно эту работу и, кажется, является простым решением
Рон
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='/'))
kinjelom
источник
Я все еще получаю ошибку «Ошибка в sys.frame (1): не так много кадров в стеке»
Майкл Бартон,
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")
bruce.moran
источник
Я получаю сообщение об ошибкеreadLink: illegal option -- e usage: readLink [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
Altabq
0

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

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) ))()
user425678
источник