首页 > 解决方案 > 我可以将变量从全局文件导入 R 吗?

问题描述

我正在将一个 R 脚本集成到一个更大的项目中,该项目与一个 Makefile 一起生成一些图形。在这个较大的项目中,我有一个名为的文件globals.mk,其中包含项目中许多其他脚本使用的全局变量。例如,我想运行的模拟次数是我想在这个 R 脚本中使用的一个全局变量。我可以将其“导入”为变量,还是需要手动定义 R 脚本中的每个变量?

编辑:这是我需要阅读的全局变量示例。

num = 100
path = ./here/is/a/path
file = $(path)/file.csv

我希望 R 脚本将变量设置num为 100(或“100”)、path“./here/is/a/path”和file“./here/is/a/path/file.csv”。

标签: rmakefile

解决方案


如果.mk文件有除直接变量扩展以外的任何内容(例如更复杂make的 -rules/tricks/functions),最好相信make为您进行扩展,然后将其读入。我发现这里有一个帖子转储所有变量内容(处理后)。

TL;博士

expand_mkvars <- function(path, aslist = FALSE) {
  stopifnot(file.exists(mk <- Sys.which("make")))
  tf <- tempfile(fileext = ".mk")
  # needed on my windows system
  tf <- normalizePath(tf, winslash = "/", mustWork = FALSE) # tempfile should suffice
  on.exit(suppressWarnings(file.remove(tf)), add = TRUE)
  writeLines(c(".PHONY: printvars",
               "printvars:",
               "\t@$(foreach V,$(sort $(.VARIABLES)), \\",
               "\t   $(if $(filter-out environment% default automatic, \\",
               "\t   $(origin $V)),$(warning $V=$($V))))"), con = tf)
  out <- system2(mk, c("-f", shQuote(path), "-f", shQuote(tf), "-n", "printvars"),
                 stdout = TRUE, stderr = TRUE)
  out <- out[grepl(paste0("^", tf), out)]
  out <- gsub(paste0("^", tf, ":[0-9]+:\\s*"), "", out)
  known_noneed <- c(".DEFAULT_GOAL", "CURDIR", "GNUMAKEFLAGS", "MAKEFILE_LIST", "MAKEFLAGS")
  out <- out[!grepl(paste0("^(", paste(known_noneed, collapse = "|"), ")="), out)]
  if (aslist) {
    spl <- strsplit(out, "=")
    nms <- sapply(spl, `[[`, 1)
    rest <- lapply(spl, function(a) paste(a[-1], collapse = "="))
    setNames(rest, nms)
  } else out
}

在行动:

expand_mkvars("~/StackOverflow/karthikt.mk")
# [1] "file=./here/is/a/path/file.csv" "num=100"                       
# [3] "path=./here/is/a/path"         

expand_mkvars("~/StackOverflow/karthikt.mk", aslist = TRUE)
# $file
# [1] "./here/is/a/path/file.csv"
# $num
# [1] "100"
# $path
# [1] "./here/is/a/path"

我没有在其他系统上测试过,所以您可能需要调整known_noneed以添加弹出的额外变量。根据您的需要,您可能能够更智能地进行过滤(例如,您的变量都没有以大写字母开头),但对于这个示例,我将其保留make为提供给我们的已知不想要的变量。


博客文章建议使用虚假目标

.PHONY: printvars
printvars:
    @$(foreach V,$(sort $(.VARIABLES)), \
       $(if $(filter-out environment% default automatic, \
       $(origin $V)),$(warning $V=$($V))))

(有些是制表符,不是所有的空格,对于 非常重要make

不幸的是,它产生的输出超出了技术上的需要:

$ /c/Rtools/bin/make.exe -f ~/StackOverflow/karthikt.mk printvars
C:/Users/r2/StackOverflow/karthikt.mk:10: .DEFAULT_GOAL=all
C:/Users/r2/StackOverflow/karthikt.mk:10: CURDIR=/Users/r2/Projects/Ford/shiny/shinyobjects/inst
C:/Users/r2/StackOverflow/karthikt.mk:10: GNUMAKEFLAGS=
C:/Users/r2/StackOverflow/karthikt.mk:10: MAKEFILE_LIST= C:/Users/r2/StackOverflow/karthikt.mk
C:/Users/r2/StackOverflow/karthikt.mk:10: MAKEFLAGS=
C:/Users/r2/StackOverflow/karthikt.mk:10: SHELL=sh
C:/Users/r2/StackOverflow/karthikt.mk:10: file=./here/is/a/path/file.csv
C:/Users/r2/StackOverflow/karthikt.mk:10: num=100
C:/Users/r2/StackOverflow/karthikt.mk:10: path=./here/is/a/path
make: Nothing to be done for 'printvars'.

所以我们需要一点过滤,因此函数中的大部分代码。


编辑:它readRenviron-to-envvar 对您来说是最好的方法,将此make调用的输出重定向到另一个文件,解析相关行,然后readRenviron在该新文件上执行并不难。由于使用了两个临时文件,这似乎更间接,但它们已被清理,因此无需担心。


推荐阅读