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

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

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

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

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

Ответ 2

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

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

Ответ 3

Я не мог заставить приложение 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))
        }
}

Ответ 4

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

Мои все в одном! (--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)
                }
            }
        }
    }
}

Ответ 9

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

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

Ответ 10

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

Ответ 11

Мне понравилось решение 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))
        }
    }
}

Ответ 12

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

библиотека scriptName

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

Ответ 13

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

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

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

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

wd <- getwd()
setwd(wd)

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

setwd(".")

Ответ 14

Вы можете обернуть 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

Ответ 15

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

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

Ответ 16

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

Ответ 17

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

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

Ответ 18

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

Ответ 19

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

Ответ 20

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

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

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

Ответ 21

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

Ответ 22

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

Ответ 23

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

Ответ 24

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