How to show stdout of a command into a viewport #928
Answered
by
meowgorithm
nihal-innsof
asked this question in
Q&A
-
I need to automate some task using go and bubbletea. And i need to print the output of the executed command in realtime into a viewport. How can this be achieved. Thanks in advance |
Beta Was this translation helpful? Give feedback.
Answered by
meowgorithm
Feb 15, 2024
Replies: 1 comment 1 reply
-
Hi! Here’s a simple program to get you started: package main
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
)
const viewportHeight = 8
type pingMsg string
type pingErrMsg struct{ err error }
type pingDoneMsg struct{}
func ping(domain string, sub chan string) tea.Cmd {
return func() tea.Msg {
// Setup the command
cmd := exec.Command("ping", "-c", "10", domain)
out, err := cmd.StdoutPipe()
if err != nil {
return pingErrMsg{err}
}
// Run the command.
if err := cmd.Start(); err != nil {
return pingErrMsg{err}
}
// Read command output as it arrives.
buf := bufio.NewReader(out)
for {
line, _, err := buf.ReadLine()
if err == io.EOF {
return pingDoneMsg{}
}
if err != nil {
return pingErrMsg{err}
}
// Send output to our program.
sub <- string(line)
}
}
}
func waitForPingResponses(sub chan string) tea.Cmd {
return func() tea.Msg {
// Send the ping to Update.
return pingMsg(<-sub)
}
}
type model struct {
sub chan string
result *string
viewport viewport.Model
}
func newModel() model {
m := model{
result: new(string),
sub: make(chan string),
viewport: viewport.New(0, viewportHeight),
}
m.appendOutput("Pinging...")
return m
}
func (m model) Init() tea.Cmd {
return tea.Batch(ping("charm.sh", m.sub), waitForPingResponses(m.sub))
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.viewport.Width = msg.Width
return m, nil
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c", "esc":
return m, tea.Quit
}
case pingMsg:
m.appendOutput(string(msg))
cmds = append(cmds, waitForPingResponses(m.sub))
case pingErrMsg:
m.appendOutput("Error: " + msg.err.Error())
case pingDoneMsg:
m.appendOutput("Done. Press ^C to exit.")
}
var cmd tea.Cmd
m.viewport, cmd = m.viewport.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m *model) appendOutput(s string) {
*m.result += "\n" + s
m.viewport.SetContent(*m.result)
m.viewport.GotoBottom()
}
func (m model) View() string {
return m.viewport.View()
}
func main() {
p := tea.NewProgram(newModel())
if _, err := p.Run(); err != nil {
fmt.Println("Oh no", err)
os.Exit(1)
}
} |
Beta Was this translation helpful? Give feedback.
1 reply
Answer selected by
nihal-innsof
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! Here’s a simple program to get you started: