我可以从全局文件将变量导入R吗?

时间:2019-06-25 17:49:26

标签: r makefile

我正在集成一个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”和{{1} }作为“ ./here/is/a/path/file.csv”。

2 个答案:

答案 0 :(得分:1)

如果.mk文件除了直接变量扩展以外(例如更复杂的make规则/技巧/功能)没有其他用途,最好信任make来执行扩展,然后阅读。我发现有一条帖子here(在处理后)转储了所有变量内容。

TL; DR

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是已知且不需要的变量中给我们。


blog post建议使用虚假目标

.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到envvar是最适合您的方法,将make调用的输出重定向到另一个文件并不困难,解析出相关的行,然后对该新文件执行readRenviron。由于使用了两个临时文件,因此似乎更加间接,但是已将它们清除,因此不必担心。

答案 1 :(得分:1)

如果可以用括号将括号括起来,那么readRenviron将读取此类文件并执行替换,将内容作为环境变量返回。

# write out test file globals2.mk which uses brace brackets
Lines <- "num = 100
path = ./here/is/a/path
file = ${path}/file.csv"
cat(Lines, file = "globals2.mk")

readRenviron("globals2.mk")
Sys.getenv("num")
## [1] "100"
Sys.getenv("path")
## [1] "./here/is/a/path"
Sys.getenv("file")
## [1] "./here/is/a/path/file.csv"

如果重要的是使用括号而不是括号,请读取globals.mk,将括号替换为括号,然后再次写出文件。

# write out test file - this one uses parentheses as in question
Lines <- "num = 100
path = ./here/is/a/path
file = $(path)/file.csv"
cat(Lines, file = "globals.mk")

# read globals.mk, perform () to {} substitutions, write out and then re-read
tmp <- tempfile()
L <- readLines("globals.mk")
cat(paste(chartr("()", "{}", L), collapse = "\n"), file = tmp)
readRenviron(tmp)