Я думаю, что это может сделать быстрая функция.
change_yaml_matter <- function(input_file, ..., output_file) {
input_lines <- readLines(input_file)
delimiters <- grep("^---\\s*$", input_lines)
if (!length(delimiters)) {
stop("unable to find yaml delimiters")
} else if (length(delimiters) == 1L) {
if (delimiters[1] == 1L) {
stop("cannot find second delimiter, first is on line 1")
} else {
# found just one set, assume it is *closing* the yaml matter;
# fake a preceding line of delimiter
delimiters <- c(0L, delimiters[1])
}
}
delimiters <- delimiters[1:2]
yaml_list <- yaml::yaml.load(input_lines[ (delimiters[1]+1):(delimiters[2]-1) ])
dots <- list(...)
yaml_list <- c(yaml_list[ setdiff(names(yaml_list), names(dots)) ], dots)
output_lines <- c(
if (delimiters[1] > 0) input_lines[1:(delimiters[1])],
strsplit(yaml::as.yaml(yaml_list), "\n")[[1]],
input_lines[ -(1:(delimiters[2]-1)) ]
)
if (missing(output_file)) {
return(output_lines)
} else {
writeLines(output_lines, con = output_file)
return(invisible(output_lines))
}
}
Где ...
- это все, что вы хотите, . Значение: если вы хотите заменить компонент output:
в yaml, то вы задаете именованный список как output=list(...)
.
Если я использую документ rmarkdown, который я использовал в предыдущем ответе , затем без изменений , он будет выглядеть так:
readLines("~/StackOverflow/1883604/62095186.Rmd")
# [1] "---"
# [2] "title: Hello"
# [3] "output: html_document"
# [4] "params:"
# [5] " intab: TRUE"
# [6] "---"
# [7] ""
# [8] "# Headline 1"
# [9] ""
# [10] "## Headline 2 `r if (params$intab) \"{.tabset}\"`"
# [11] ""
# [12] "### Headline 3 in a tab"
# [13] ""
# [14] "### Headline 4 in a tab"
# [15] ""
# [16] "### Headline 5 in a tab"
# [17] ""
# [18] ""
И чтобы измените часть output
, я добавлю вложенный именованный список как:
change_yaml_matter("~/StackOverflow/1883604/62095186.Rmd",
output=list(ioslides_presentation=list(widescreen=TRUE)))
# [1] "---"
# [2] "title: Hello"
# [3] "params:"
# [4] " intab: yes"
# [5] "output:"
# [6] " ioslides_presentation:"
# [7] " widescreen: yes"
# [8] "---"
# [9] ""
# [10] "# Headline 1"
# [11] ""
# [12] "## Headline 2 `r if (params$intab) \"{.tabset}\"`"
# [13] ""
# [14] "### Headline 3 in a tab"
# [15] ""
# [16] "### Headline 4 in a tab"
# [17] ""
# [18] "### Headline 5 in a tab"
# [19] ""
# [20] ""
Вы можете изменить практически любую часть материала yaml. (Единственное, что вы не можете изменить, я подозреваю, это если у вас есть параметры yaml с именем input_file
или output_file
. Если у вас действительно есть файлы Rmd с этими параметрами верхнего уровня yaml, вы можете легко переименовать именованные аргументы здесь должно быть что-то еще, например Mxyzptlk
и что-то еще ... вы вряд ли увидите их в производстве.)
Примечания:
- Это ничего не сохраняло в файл, вы должны сделать это сами. Добавьте
output_file="path/to/new.RMd"
к вашему вызову, и он запишет новый файл. - Когда вы включите
output_file=
в аргументы, если вы решите не улавливать возвращаемое значение, оно будет ничего не вернуть. Это связано с invisible
в моем возвращении; если вы действительно хотите увидеть и сохранить, либо захватите переменную и посмотрите на нее, либо заключите вызов функции в скобки, как в (change_yaml_matter(...))
.
Уловка для YAML заключается в том, чтобы знать, что yaml::
будет рассматривать каждый верхний уровень как именованный элемент списка, и его содержимое будет рекурсивно списками таким же образом. Например,
str(yaml::yaml.load("
---
top1:
level2a:
level3a: 123
level3b: 456
level2b: 789
top2: quux
---"))
# List of 2
# $ top1:List of 2
# ..$ level2a:List of 2
# .. ..$ level3a: int 123
# .. ..$ level3b: int 456
# ..$ level2b: int 789
# $ top2: chr "quux"
Чтобы назначить новые значения, просто укажите вложенные именованные списки.