How to change location and name of files generated by engines in Rmarkdown in each chunk

I setup an engine on RMarkdown:

knitr::knit_engines$set(mplus = function(options) {
code <- paste(options$code, collapse = "\n")
fileConn<-file("mplus_code.inp")
writeLines(code, fileConn)
close(fileConn)
out  <- system2("/opt/mplus/8.10/mplus", "mplus_code.inp")
fileConnOutput <- file("mplus_code.out")
mplusOutput <- readLines(fileConnOutput)
knitr::engine_output(options, code, mplusOutput)
})

I am running it as follows:

```{cat bcfa, engine.opts = list(file = "assets/code/mplus/mplus_bcfa.inp", lang = "mplus"), name = "mplus_bcfa.inp"}
TITLE:  TWO-FACTOR MODEL OF POLITICAL ATTITUDES
DATA:
FILE IS assets/data/BAYESCFA.dat;
VARIABLE:
NAMES ARE X1-X10;
DEFINE:  STANDARDIZE X1-X10;
ANALYSIS: ESTIMATOR=BAYES;
      PROCESSORS = 10;  ! used to increase computational speed
      FBITER = 10000;  ! over-rides Mplus convergence criterion
MODEL:
 CONSERV BY X1* X2 X3 X4 X5;
   LIBERAL BY X6* X7 X8 X9 X10;
   CONSERV-LIBERAL@1;
! SPECIFICATION OF CROSS-LOADINGS:
   CONSERV BY X6-X10*0 (A1-A5);
   LIBERAL BY X1-X5*0 (B1-B5);
MODEL PRIORS:
    A1-B5 ~ N(0,.01);
OUTPUT: TECH1 TECH8 STAND;
PLOT:   TYPE=PLOT2; 
```

In other words, this chunk will write the syntax in the assets/code/mplus/mplus_bcfa.inp file and show it on the xaringan slides with the Mplus format highlights.

I would like to call that chunk and run it, generating the output file with the name and in the location decided by me. I tried it using this following chunk (for the next slide) so it shows the output:

```{mplus, eval=TRUE, echo=F, ref.label='bcfa', root.dir = "assets/code/mplus"}

I would like this chunk to create the file mplus_bcfa.out in the folder assets/code/mplus. Although, it does not do it, it just creates the file mplus_code.out (as define in the knitr engine configuration) and places it in the root of the RMarkdown folder.

To add some more context, I tried also to define input and output within the engine options:

knitr::knit_engines$set(mplus = function(options) {
 code <- paste(options$code, collapse = "\n")
 fileConn<-file(options$input)
 writeLines(code, fileConn)
 close(fileConn)
 out  <- system2("/opt/mplus/8.10/mplus", options$input)
 fileConnOutput <- file(options$output)
 mplusOutput <- readLines(fileConnOutput)
 knitr::engine_output(options, code, mplusOutput)
})

And then call then within the chunk options (i.e., input and output:

```{mplus, eval=TRUE, echo=F, ref.label='bcfa', root.dir = "assets/code/mplus", input = "assets/code/mplus/mplus_bcfa.inp", output = "assets/code/mplus/mplus_bcfa.out"}
```

But it did not work...

You can have a look at the eng_exec() we introduced in knitr

This could help for your engine. Most of the options to pass to engine, are supposed to be in engine.opts as a list by the way

1 Like

Thank you, @cderv. I was able to reach a solution:

knitr::knit_engines$set(mplus = function(options) {
    # Get the input and output file names from the chunk options
  inputFile <- options$input
  outputFile <- options$output

  # Create a connection to the input file and write the code to it
  fileConn <- file(inputFile)
  writeLines(paste(options$code, collapse = "\n"), fileConn)
  close(fileConn)

  # Run the Mplus command and specify the output file in the command
  system2("/opt/mplus/8.10/mplus", args = c(inputFile, ">", outputFile))

  # Read the output file
  fileConnOutput <- file(outputFile)
  mplusOutput <- readLines(fileConnOutput)
  close(fileConnOutput)

  # Return the output
  knitr::engine_output(options, options$code, mplusOutput)
})

However, with this solution I must always provide input and output arguments in my chunks, for example:

```{mplus bcfa, eval=F, results='hide', name = "mplus_bcfa.inp", input = 
 "assets/code/mplus/mplus_bcfa.inp", output = "assets/code/mplus/mplus_bcfa.out"}
TITLE:  TWO-FACTOR MODEL OF POLITICAL ATTITUDES
DATA:
  FILE IS /home/ubuntu/Disciplines/cpae/assets/data/BAYESCFA.dat;
VARIABLE:
  NAMES ARE X1-X10;
DEFINE:  STANDARDIZE X1-X10;
ANALYSIS: ESTIMATOR=BAYES;
          PROCESSORS = 10;  ! used to increase computational speed
          FBITER = 10000;  ! over-rides Mplus convergence criterion
MODEL:
    CONSERV BY X1* X2 X3 X4 X5;
    LIBERAL BY X6* X7 X8 X9 X10;
    CONSERV-LIBERAL@1;
    ! SPECIFICATION OF CROSS-LOADINGS:
    CONSERV BY X6-X10*0 (A1-A5);
    LIBERAL BY X1-X5*0 (B1-B5);
MODEL PRIORS:
    A1-B5 ~ N(0,.01);
OUTPUT: TECH1 TECH8 STAND;
PLOT:   TYPE=PLOT2; 
```

And even if I call the chunk later, I will have to define input and output again. In this case:

```{mplus, eval=TRUE, echo=F, ref.label='bcfa', input = "assets/code/mplus/mplus_bcfa.inp", output = "assets/code/mplus/mplus_bcfa.out"}
```

1 - Any advice on how to define that if no input or output are provided to use temporary folder?

2- Is it possible to modify existing engines to have a similar behavior in terms of writing the code in the input file and results of the code an output file? Sorry for all my questions, I am using RMarkdown with the xaringan package for educational purposes.

You just need to use default value in your engine if nothing options value is provided. Won't that work ?

inputFile <- options$input
if (is.null(inputFile)) { 
...
}

Though, know that chunk options can be defined globally too if that could help.

I don't understand what you are asking here ? What engine to you want to modify ?