Search code examples
rr-markdownknitrexecutableshebang

Is it possible to create a self-rendering Rmarkdown document?


For typical R scripts the shebang syntax can be used to run the code within. With a file file.R

#!/usr/bin/env Rscript

<some R code here>

Running ./file.R will execute the code.

But can somehow the same be done with R-markdown? So that with a file named file.Rmd:

#!/usr/bin/env <some command>

<some markdown>
<some R code here>
<some markdown again>

Running ./file.Rmd would produce file.md?


Solution

  • You can treat an Rmd file as an Rscript. For instance, assume that your Rmd file looks like this

    ---
    title: "Untitled"
    author: "ekoam"
    date: "`r Sys.Date()`"
    output: html_document
    ---
    
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = TRUE)
    ```
    
    ## R Markdown
    
    This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see <http://rmarkdown.rstudio.com>.
    
    When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
    
    ```{r cars}
    summary(cars)
    ```
    
    ## Including Plots
    
    You can also embed plots, for example:
    
    ```{r pressure, echo=FALSE}
    plot(pressure)
    ```
    
    Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot.
    

    You can then prepend the following code to that Rmd file

    #!/usr/bin/env Rscript
    
    args <- commandArgs()
    fname <- normalizePath(sub("--file=", "", args[grepl("--file=", args)]))
    thisfile <- readLines(fname)
    newfname <- paste0(tempdir(), "/", basename(fname))
    writeLines(thisfile[-1:-which(thisfile == "q(\"no\")")], newfname)
    rmarkdown::render(newfname, output_dir = dirname(fname))
    q("no")
    

    The trick here is q("no"). This line terminates the R session, and, thus, whatever written after it will be ignored. Such an effect also means high flexibility for coding because you can write almost any valid R code before that q("no"). The code above simply creates another temporary Rmd file with the same content as what is after q("no"). Then, we rmarkdown::render that temporary file and dump the output to the current directory.

    The complete Rmd file looks like this

    #!/usr/bin/env Rscript
    
    args <- commandArgs()
    fname <- normalizePath(sub("--file=", "", args[grepl("--file=", args)]))
    thisfile <- readLines(fname)
    newfname <- paste0(tempdir(), "/", basename(fname))
    writeLines(thisfile[-1:-which(thisfile == "q(\"no\")")], newfname)
    rmarkdown::render(newfname, output_dir = dirname(fname))
    q("no")
    
    ---
    title: "Untitled"
    author: "ekoam"
    date: "`r Sys.Date()`"
    output: html_document
    ---
    
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = TRUE)
    ```
    
    ## R Markdown
    
    This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see <http://rmarkdown.rstudio.com>.
    
    When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
    
    ```{r cars}
    summary(cars)
    ```
    
    ## Including Plots
    
    You can also embed plots, for example:
    
    ```{r pressure, echo=FALSE}
    plot(pressure)
    ```
    
    Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot.