使用 R 包安装可执行脚本

Installing executable scripts with R package

在大多数脚本语言(例如 Ruby、Python 等)中,包管理器(例如 gem、pip 等)可以将脚本安装为可执行文件,并且 link 它们到 PATH 变量中引用的目录(例如 /usr/local/bin)。这会将那些可执行脚本变成 shell 命令,用户可以 运行 以独立的方式在编程界面之外执行这些命令。

不知R中是否也有这种可能。鉴于 R 使用标准 Makefile,我想一定有办法做到这一点,尽管是非标准的。我已经知道我们可以在 R 脚本 using the docopt package 中读取命令行参数。但是有没有办法在安装包时将脚本安装为可执行文件?

如果能在这个主题上发挥主导作用就好了,但是来自 CRAN 的一个工作示例也足够了。

简短(而且非常悲伤)的回答:你不能。但请继续阅读。

推理:R 只会将包内容写入其自己的 .libPaths() 目录(或第一个目录,以防给出多个),或用户指定的目录。

所以,说 /usr/local/bin/ 简直遥不可及。这是一种防御策略。

这也很可悲——我写 littler (also CRAN page) 正是为了这个目的:可执行的 R 脚本。我们有 数十名 的工作人员从 cron 工作中调用。那么我们该怎么办? 一次性 soft-link 来自包含脚本的包的 scripts/ 子目录到 /usr/local/bin。在软件包升级时,link 作为软 link.

持续存在

这也是我为 所做的,例如 所有 examples shipping with littler and more from other packages. Many of them use docopt

我有类似的目标,这是我确定的解决方案。我在我的 R 包中添加了一个函数,它通过并(详细地)创建了从 ~/.local/bin 到我的包 exec 文件夹中的每个脚本的符号链接。

其他合理的默认位置可能是 ~/.local/lib/R/bin~/bin,但我最喜欢 ~/.local/bin

所以在安装包后,我将用户引导至 运行

Rscript -e 'mypackage::install_executable_scripts()'
#' @export
install_executable_scripts <- function(into = "~/.local/bin", overwrite = FALSE) {
  scripts <- dir(system.file("exec", package = "mypackage"),
                 full.names = TRUE)
  if (!dir.exists(into)) dir.create(into)
  into <- normalizePath(into)

  dests <- file.path(normalizePath(into), basename(scripts))
  if (any(already_exist <- file.exists(dests))) {
    if (overwrite) {
      to_del <- dests[already_exist]
      cli::cat_bullet("Deleting existing file: ", to_del,
                      bullet_col = "red")
      unlink(to_del)
    } else {
      cli::cat_bullet(sprintf(
        "Skipping script '%s' because a file by that name already exists at the destination",
        basename(scripts[already_exist])))
      scripts <- scripts[!already_exist]
      dests   <-   dests[!already_exist]
    }
  }
  if (length(scripts)) {
    file.symlink(scripts, dests)
    cli::cat_line("Created symlinks:")
    cli::cat_bullet(dests, " ->\n    ", scripts, bullet_col = "green")
  } else
    cli::cat_line("Nothing installed")

  PATHS <- normalizePath(strsplit(Sys.getenv("PATH"), ":", fixed = TRUE)[[1]],
                         mustWork = FALSE)
  if(!into %in% PATHS)
    warning(sprintf("destination '%s' is not on the PATH", into))
}