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?
Possibly related:
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With