14.1 Write the chunk content to a file via the cat engine

Sometimes it could be useful to write to some files from inside the Rmd. If this could be achieved using pure R code in a r code chunk, knitr as a little unknown gem for this : the cat engine.

14.1.1 About the cat engine

The cat engine allows to write to a file some codes from a Rmarkdown chunk. The file path is passed as an argument in the generic engine.opts chunk options: engine.opts = list(file = <file to write to>). The element in this list will be pass to base::cat() function allowing to customize how the content is written to file.

In this example, we will write to a temp file with fs::path_temp().

{cat, engine.opts = list(file = fs::path_temp('script.R'))}
my_function <- function(x){ x+1 }


The content can be used by filling a chunk from that file. This is another trick made possible using the code chunk option. You’ll see the content of the file

{r, code = readLines(fs::path_temp('script.R'))}


This will render

my_function <- function(x) {
x + 1
}

As the code above is executed, the function is available in any following r chunk.

my_function(5)
rm(my_function)
## [1] 6

And the file can be sourced as usual too.

source(fs::path_temp("script.R"))
my_function(5)
rm(my_function)
## [1] 6

Writing to an R script is not really a real use case example and the cat engine can handle any format.

Here, we use it to write some yaml to a file. The default behavior for cat engine is to show nothing in the output document. However, here we will also show the content in the output R Markdown file. The cat engine understand the field lang in engine.opts option or directly class.source chunk option for html outputs (see Section 6.3). You can provide any language supported by the syntax highlighting. These two chunks are equivalent:

{cat, engine.opts = list(file = fs::path_temp('demo.yml'), lang = "yaml")}
a:
aa: something
bb: 1
b:
aa: somethingelse
bb: 2

{cat, engine.opts = list(file = fs::path_temp('demo.yml')), class.source = "yaml"}
a:
aa: something
bb: 1
b:
aa: somethingelse
bb: 2


They will write to file and print the code with the chosen syntax highlighting, here yaml

a:
aa: something
bb: 1
b:
aa: somethingelse
bb: 2

As previously, the file can be accessed later on and read into R

yaml::read_yaml(fs::path_temp("demo.yml"))
## $a ##$a$aa ## [1] "something" ## ##$a$bb ## [1] 1 ## ## ##$b
## $b$aa
## [1] "somethingelse"
##
## $b$bb
## [1] 2

14.1.2 Generate a template file from inside the rmarkdown

As detailed in section 6.3, css chunk can directly be used to apply custom style. However, it is also possible to provide a custom css file to pandoc directly to be used with the html output and the cat engine can be used to write this css file from the Rmarkdown.

One important key is that all chunks are executed before pandoc conversion. This means it is possible in the yaml header to link to a file that does not exist and will be created when rendering the document.

This complete example shows how to generate my_custom.css from a chunk in the document while telling pandoc to use this css with the yaml field css: my_custom.css

---
title: "CSS generated in chunk"
output:
html_document:
css: my_custom.css
---

The chunk below will be written to my_custom.css that will be used during
pandoc conversion, as specified in the yaml header above.

{cat, engine.opts = list(file = "my_custom.css")}
h2 {
color: blue;
}


## And this title will blue.

This could also be used to generate any file that can be given to pandoc, like an in_header tex file for a pdf_document output.3

---
title: "Generate a header template for latex from a chunk"
author: "Jane Doe"
output:
pdf_document:
includes:
---

# how it works

{cat, engine.opts=list(file = 'header.tex')}
\usepackage{fancyhdr}
\usepackage{lipsum}
\pagestyle{fancy}
\fancyfoot[CO,CE]{And this is a fancy footer}
\fancyfoot[LE,RO]{\thepage}
\fancypagestyle{plain}{\pagestyle{fancy}}


However, the aim here is to add the author name from the yaml header into the
tex template. Some R code can't be passed into the cat engine because it won't
be executed, but it is possible to append some more to the created file.

Here we generate the line to append in a R chunk using knitr::knit_expand
templating tool. Any other way to build a string will work obviously.

{r, include = FALSE}
author_template <- knitr::knit_expand(
delim = c("%", "%"))


Then, providing append = TRUE and using the code chunk option to provide
content to the chunk

{cat, engine.opts=list(file = 'header.tex', append = TRUE), code = author_template}


This will generate a footer with the author name that we got from the yaml
\lipsum[1-30]