Stream system() output to Shiny front-end (continuously)
Asked Answered
G

1

9

How can I capture the output of an ongoing system() operation and stream it to the Shiny front-end in "real-time"?

intern=T captures the entire output in a character vector, but I would prefer to "listen" to the system output as it happens.

library(shiny) 
ui <- fluidPage(
   titlePanel("Stream the system output"),
   sidebarLayout(
      sidebarPanel(
         actionButton("btn1",label = "Let's stream")
      ),
      mainPanel(
         textOutput("textstream_output")
      )
   )
)
server <- function(input, output, session) {
  rv <- reactiveValues("textstream"=c(""))
  output$textstream_output <- renderText({
    rv$textstream
  })
  observeEvent(input$btn1,{
  # problem: is evaluated after finish, not during operation  
    rv$textstream <- system("Rscript -e \"for(i in 1:5){ print(Sys.time()); Sys.sleep(1);  };\"",
                            intern = T)
  })
}
shinyApp(ui = ui, server = server)

When running the system command with intern=F, the R console continuously updates once per second. How can I establish that in Shiny, ideally without having to slice the system call into smaller chunks?

enter image description here

Possibly related:

Gamete answered 1/6, 2018 at 20:29 Comment(0)
A
6

reactiveTimer provides one approach. My guess is that your approach doesn't work because observeEvent only updates the reactive object once evaluation of the expression is completed. Here's my approach. I create a script that I want to run in the background, so_script.R, and divert the output to so_output.txt. We wish to see the contents of so_output.txt while the script is running.

cat('sink(file = "so_output.txt")
  for (i in 1:10) {
    cat(format(Sys.time(), format = "%H:%M:%S"), "\n")
    Sys.sleep(1)
  }
  cat("*** EOF ***\n")
  sink()
', file = "so_script.R")

Here's the Shiny app:

library(shiny) 
ui <- fluidPage(
   titlePanel("Stream the system output"),
   sidebarLayout(
      sidebarPanel(
         actionButton("btn_start",label = "Let's stream"),
         actionButton("btn_stop",label = "Stop")
      ),
      mainPanel(
         htmlOutput("textstream_output")
      )
   )
)
server <- function(input, output, session) {
  rv <- reactiveValues(textstream = c(""),
                       timer = reactiveTimer(1000),
                       started = FALSE)
  observeEvent(input$btn_start, { 
    rv$started <- TRUE
    system2("Rscript", "so_script.R", wait = FALSE)
  })
  observeEvent(input$btn_stop, { rv$started <- FALSE })
  observe({
    rv$timer()
    if (isolate(rv$started))
      rv$textstream <- paste(readLines("so_output.txt"), collapse = "<br/>")
  })
  output$textstream_output <- renderUI({
    HTML(rv$textstream)
  })
}
shinyApp(ui = ui, server = server)

Each time the timer fires, we read in the contents of so_output.txt if streaming has started. Output:

enter image description here

Assets answered 3/6, 2018 at 12:39 Comment(1)
Hi, I'm kinda digging up this old post but it had been helpful. I am currently wanting to implement something similar, but would like the reading from so_output.txt to stop without having to click a stop button. Do you think there is a way of detecting this ? Thanks in advanceForenoon

© 2022 - 2024 — McMap. All rights reserved.