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

Sometimes it could be useful to write the content of a code chunk to an external file, and use this file later in other code chunks. Of course, you may do this via the R functions like writeLines(), but the problem is that when the content is relatively long, or contains special characters, the character string that you would pass to writeLines() may look awkward. Below is an example of writing a long character string to a file my-file.txt:

writeLines("This is a long character string.
It has multiple lines. Remember to escape
double quotes \"\", but 'single quotes' are OK.
I hope you not to lose your sanity when thinking
about how many backslashes you need, e.g., is it
'\t' or '\\t' or '\\\\t'?", 
  con = "my-file.txt")

This problem has been greatly alleviated since R 4.0.0, because R started to support raw strings in r"( )" (see the help page ?Quotes), and you do not need to remember all the rules about special characters. Even with raw strings, it can still be a little distracting for readers to see a long string written to a file explicitly in a code chunk.

The cat engine in knitr has provided a way for you to present text content in a code chunk and/or write it to an external file, without thinking about all the rules about R’s character strings (e.g., you need double backslashes when you need a literal backslash).

To write the chunk content to a file, specify the file path in the chunk option engine.opts, e.g., engine.opts = list(file = 'path/to/file'). Under the hood, the list of values specified in engine.opts will be passed to the function base::cat(), and file is one of the arguments of base::cat().

Next we will present three examples to illustrate the use of the cat engine.

15.6.1 Write to a CSS file

As shown in Section 7.3, you can embed a css code chunk in an Rmd document to style elements with CSS. An alternative way is to provide a custom CSS file to Pandoc via the css option of some R Markdown output formats such as html_document. The cat engine can be used to write this CSS file from Rmd.

This example below shows how to generate a file custom.css from a chunk in the document, and pass the file path to the css option of the html_document format:

---
title: "Create a CSS file from a code chunk"
output: 
  html_document:
    css: custom.css
---

The chunk below will be written to `custom.css`, which
will be used during the Pandoc conversion.

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

## And this title will blue

The only difference between the css code chunk approach and this approach is that the former approach writes the CSS code in place (i.e., in the place of the code chunk), which is inside the <body> tag of the output document, and the latter approach writes CSS to the <head> area of the output document. There will not be any practical visual differences in the output document.

15.6.2 Include LaTeX code in the preamble

In Section 6.1, we introduced how to add LaTeX code to the preamble, which requires an external .tex file. This file can also be generated from Rmd, and here is an example:

---
title: "Create a .tex file from a chunk"
author: "Jane Doe"
classoption: twoside
output: 
  pdf_document:
    includes:
      in_header: preamble.tex
---

# How it works

Write a code chunk to a file `preamble.tex` to define
the header and footer of the PDF output document:

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

\lipsum[1-15]

# More random content

\lipsum[16-30]

In the LaTeX code in the cat code chunk above, we have defined the header and footer of the PDF document. If we also want to show the author name in the footer, we can append the author information to preamble.tex in another cat code chunk with options engine.opts = list(file = 'preamble.tex', append = TRUE) and code = sprintf('\\fancyfoot[LO,RE]{%s}', rmarkdown::metadata$author). To understand how this works, recall that we mentioned earlier in this section that engine.opts is passed to base::cat() (so append = TRUE is passed to cat()), and you may understand the chunk option code by reading Section 16.2.

15.6.3 Write YAML data to a file and also display it

By default, the content of the cat code chunk will not be displayed in the output document. If you also want to display it after writing it out, set the chunk option class.source to a language name. The language name is used for syntax highlighting. In the example below, we specify the language to be yaml:

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

Its output is displayed below, and it also generated a file demo.yml.

a:
  aa: "something"
  bb: 1
b:
  aa: "something else"
  bb: 2

To show the file demo.yml is really generated, we can try to read it into R with the yaml package (Stephens et al. 2020):

xfun::tree(yaml::read_yaml("demo.yml"))
## List of 2
##  |-a:List of 2
##  |  |-aa: chr "something"
##  |  |-bb: int 1
##  |-b:List of 2
##     |-aa: chr "something else"
##     |-bb: int 2

References

Stephens, Jeremy, Kirill Simonov, Yihui Xie, Zhuoer Dong, Hadley Wickham, Jeffrey Horner, reikoch, Will Beasley, Brendan O’Connor, and Gregory R. Warnes. 2020. Yaml: Methods to Convert R Data to Yaml and Back. https://github.com/viking/r-yaml/.