2

I am working on a .Rmd document where we show people how to use the command line. This includes bash-based code blocks like this:

```{bash}
echo "this is a test"
```

However, we would like to parameterize this, so have something like

---
params:
    testparam: "this would echo something else"
---

```{bash}
echo params$testparam
```

This doesn't work, however, because inside a bash code block the parameters don't exist. Is there a way to use parameters in such cases, in essence evaluating params$testparam before knitr considers the fact that it's inside a non-R code block?

Ideally, the solution would make the following possible:

```{bash}
echo params$testparam
```

turns into

<pre class="bash"><code>echo "this would echo something else"</code></pre>
<pre><code>## this would echo something else</code></pre>
LukasKawerau
  • 1,071
  • 2
  • 23
  • 42
  • (Untested) If this is going to be pre-processed by `knitr` anyway, you might have a first `r` block that does something like `do.call(Sys.setenv, params)`? It might transfer over, not certain. – r2evans Mar 28 '19 at 18:30

2 Answers2

2

As I hoped, this small hack works:

---
params:
    testparam: "this would echo something else"
---

```{r, echo = FALSE, include = FALSE}
if (length(params) > 0) do.call(Sys.setenv, params)
```

```{bash}
echo $testparam
```

Yielding

<pre class="bash"><code>echo $testparam</code></pre>
<pre><code>## this would echo something else</code></pre>

This will fail if you pass non-simple objects. I haven't tested vectors/lists/frames, though I suspect they will fail spectacularly ... but since you're using bash in that chunk, I'm assuming its parameters are less-complex.

If you have some params that are complex but never to be used in your bash chunks, you might consider a naming convention, something like:

---
params:
    bashtestparam: "this would be one thing"
    testparam: "this would echo something else"
---

```{r, echo = FALSE, include = FALSE}
p <- params[ grepl("^bash", names(params)) ]
names(p) <- gsub("^bash", "", names(p))
if (length(p) > 0) do.call(Sys.setenv, p)
```

```{bash}
echo $testparam
```

which yields

<pre class="bash"><code>echo $testparam</code></pre>
<pre><code>## this would be one thing</code></pre>
r2evans
  • 141,215
  • 6
  • 77
  • 149
  • I just fixed a small bug that will error out if there are no `params` (or no *matching* `params` into `p`). – r2evans Mar 28 '19 at 18:59
  • This is great, thank you! The remaining question for me, much like with the answer by @Anders, how to get the value of `testparam` into the code block. In your answer, the code block reads `echo $testparam`, but I want to show people what happens if they write `echo "this would be one thing"`, I'm not sure what to do there. – LukasKawerau Mar 28 '19 at 19:01
  • 1
    That's a different thing ... that might require knitting a child document and including its output into the current document. See `knitr::knit_child`, I'd think. – r2evans Mar 28 '19 at 19:04
  • 1
    Thank you, I will check that out then. I apologize for not being clearer in the beginning. – LukasKawerau Mar 28 '19 at 19:07
1

The following .Rmd may suffice? I use the system() within a R block:

---
output: html_document
params:
    testparam: "this would echo something else"
---

# Header

Some text.

```{bash}
echo "this is a test"
```

Some more text.

```{r}
cat(system(paste0("echo '", params$testparam, "'"), intern = TRUE), sep = "\n")
```

enter image description here

Strong inspiration from here. Of course, the bash command is not readily visible, but I suspect one can work around that.

EDIT:

With a bit of a work-around/hack, you can render the bash code in the following manner:

```{r bashCode, results="asis", echo=FALSE}
bash_code <- sprintf("echo '%s'", params$testparam)
cat("<pre class='bash'><code>",
    bash_code,
    "</code></pre>")
```

```{r bashOutput, echo=FALSE}
cat(system(bash_code, intern = TRUE), sep = "\n")
```

enter image description here

So we generate the bash code as a character and cat the bash code wrapped in appropriate html while telling knitr to interpret the result 'asis' (making the results appear as code). As we also suppress the R code itself (echo=FALSE), the results appear as code only. Next, in the subsequent chunk, we again suppress the printing of the code but get the output of the system command which is interpreted in the standard manner.

You can of course utilize the trick from @r2evans together with this as well.

Anders Ellern Bilgrau
  • 9,928
  • 1
  • 30
  • 37
  • This works and _almost_ is exactly what I was looking for. But the bash command not being visible is something I would indeed need do work around, and I'm not sure how. Since I would probably do that in an additional code block with `eval=FALSE` above your solution, how would I get the `params$testparam` into it? – LukasKawerau Mar 28 '19 at 18:48