我有几个Rmd文档,除了标题外,它们都具有相同的YAML前页。我怎么能保持这个frontmatter在一个文件,并有它用于所有的文档?它变得相当大,我不想每次调整frontmatter时都保持每个文件的同步。
I want to still
- 在RStudio中使用Knit按钮/Ctrl+Shift+K快捷键来编译
- 保持整个设置可移植:希望避免编写自定义输出格式或重写
rstudio.markdownToHTML
(因为这将要求我携带.Rprofile
太)
common.yaml:
author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
# many other options
示例文档
----
title: On the Culinary Preferences of Anthropomorphic Cats
----
I do not like green eggs and ham. I do not like them, Sam I Am!
所期望的输出:编译的示例文档(即HTML或PDF),已编译的元数据在common.yaml
注入。YAML中的R代码(在本例中是日期)将被编译作为奖励,但这不是必需的(我只将它用于我并不真正需要的日期)。
选项/解决方案?
我还没有完全使这些工作。
- 用标记可以创建一个
_output.yaml
来放置常见的YAML元数据,但这会将所有的元数据放在YAML的output:
下,所以只适用于html_document:
和pdf_document:
下的选项,而不是像作者,日期,… 写一个knitr块来导入YAML,例如
---- title: On the Culinary Preferences of Anthropomorphic Cats ```{r echo=F, results='asis'} cat(readLines('common.yaml'), sep='n') ``` ---- I do not like green eggs and ham. I do not like them, Sam I Am!
如果我
knitr('input.Rmd')
然后pandoc
输出,但如果我使用Rstudio中的Knit按钮(我假设调用render
)则不行,因为这在运行knitr之前首先解析元数据,并且元数据是畸形的,直到knitr已经运行。- Makefile:如果我足够聪明,我可以写一个Makefile或其他东西来注入
common.yaml
到input.Rmd
,然后运行rmarkdown::render()
,并以某种方式将其连接到Rstudio的Knit按钮,也许以某种方式将这个Rstudio配置保存到.Rproj
文件中,这样整个事情是可移植的,而不需要我编辑.Rprofile
。但是我不够聪明。
EDIT:我尝试了最后一个选项,并将Makefile连接到Build命令(Ctrl+Shift+B)。但是,每次我通过Ctrl+Shift+B使用它时,这将构建相同的目标,并且我想构建与我目前在编辑器中打开的Rmd文件相对应的目标[至于Ctrl+Shift+K]。
找到了两个可移植的选项(即不需要.Rprofile
定制,最小限度地重复YAML前文):
- 你可以在命令行上给pandoc提供通用的yaml !分析!
- 您可以将元数据的
knit:
属性设置为您自己的函数,以便更好地控制Ctrl+Shift+ k时发生的事情。
选项1:通用YAML到命令行。
把所有通用的YAML放到它自己的文件
common.yaml
:
---
author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
---
注意它是完整的,即需要---
。
然后在文档中,你可以指定YAML作为pandoc的最后一个参数,它将应用YAML(参见这个github问题)
in example.rmd
:
---
title: On the Culinary Preferences of Anthropomorphic Cats
output:
html_document:
pandoc_args: './common.yaml'
---
I do not like green eggs and ham. I do not like them, Sam I Am!
你甚至可以把html_document:
的东西放在_output.yaml
,因为rmarkdown
会把它放在output:
文件夹下的所有文档。通过这种方式,在所有使用此标题的文档之间可以有而没有重复的YAML。
优点:
- 没有重复的YAML标题。
- 非常干净
缺点:
- 通用的YAML没有通过
knit
,所以上面的日期字段不会被解析。您将得到字符串"r format(Sys.time(), format='%Y-%m-%d %H:% m:%S %z')"作为您的日期。 来自同一个github问题:
首先看到的元数据定义保持不变,即使稍后解析冲突的数据。
根据您的设置,这可能是一个问题。
选项2:覆盖knit
命令
这允许更大的控制,虽然有点麻烦/棘手。
这个链接和这个链接提到了一个未记录的特性:当点击Rstudio的"Knit"按钮时,YAML的knit:
部分将被执行。
总之:
- 定义一个函数
myknit(inputFile, encoding)
,它将读取YAML,将其放入RMD,并在结果上调用render
。保存在自己的文件myknit.r
. 在
example.rmd
的YAML中,添加knit: (function (...) { source('myknit.r'); myknit(...) })
它似乎必须在一行上。使用
source('myknit.r')
而不是仅仅将函数定义放入YAML的原因是为了可移植性。如果我修改myknit.r
,我不必修改每个文档的YAML。这样,所有文档必须在其标题中重复的唯一通用 YAML是knit
行;所有其他常见的YAML可以留在common.yaml
.
然后按Ctrl+Shift+K工作,我希望从Rstudio内。
进一步说明:
-
myknit
可能只是一个系统调用make
,如果我有一个makefile设置。 - 注入的YAML将通过
rmarkdown
传递,因此是针织的,因为它在调用render
之前被注入。预览窗口:只要
myknit
产生(单个)消息Output created: path/to/file.html
,那么文件将显示在预览窗口我发现在输出中只能有一个这样的消息[不是多个],或者你没有预览窗口。因此,如果您使用
render
(它使"Output created: basename.extension")消息而最终生成的文件实际上在其他地方,则需要通过render(..., quiet=T)
或suppressMessages(render(...))
(前者也抑制knitr progress和pandoc输出)来抑制此消息,并使用正确的路径创建您的自己的消息。
优点:
- YAML首页是针织的
- 比选项1更多的控制,如果你需要做自定义预处理/后处理。
缺点:
- 比选项1更努力一点
-
knit:
行必须在每个文档中重复(尽管source('./myknit.r')
至少可以将函数定义存储在一个中心位置)
这是子孙后代的设置。为了可移植性,您只需要携带myknit.r
和common.yaml
。不需要.Rprofile
或特定于项目的配置。
example.rmd
:
---
title: On the Culinary Preferences of Anthropomorphic Cats
knit: (function (...) { source('myknit.r'); myknit(...) })
---
I do not like green eggs and ham. I do not like them, Sam I Am!
common.yaml
[示例]:
author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
myknit.r
:
myknit <- function (inputFile, encoding, yaml='common.yaml') {
# read in the YAML + src file
yaml <- readLines(yaml)
rmd <- readLines(inputFile)
# insert the YAML in after the first ---
# I'm assuming all my RMDs have properly-formed YAML and that the first
# occurence of --- starts the YAML. You could do proper validation if you wanted.
yamlHeader <- grep('^---$', rmd)[1]
# put the yaml in
rmd <- append(rmd, yaml, after=yamlHeader)
# write out to a temp file
ofile <- file.path(tempdir(), basename(inputFile))
writeLines(rmd, ofile)
# render with rmarkdown.
message(ofile)
ofile <- rmarkdown::render(ofile, encoding=encoding, envir=new.env())
# copy back to the current directory.
file.copy(ofile, file.path(dirname(inputFile), basename(ofile)), overwrite=T)
}
在example.rmd
的编辑器中按Ctrl+Shift+K/Knit将编译结果并显示预览。我知道它正在使用common.yaml
,因为结果包括日期和作者,而example.rmd
本身没有日期或作者。
@mathematical提出的第一个解。咖啡是一个很好的方法,但是它给出的例子不适合我(可能是因为语法改变了)。如上所述,这可以通过在YAML头文件中提供pandoc参数来实现。例如,
这是header.yaml
文件的内容:
title: "Crime and Punishment"
author: "Fyodor Dostoevsky"
添加到RMarkdown文件的开头:
---
output:
html_document:
pandoc_args: ["--metadata-file=header.yaml"]
---
参见pandoc手册中的--metadata-file
参数