charmbracelet/bubbletea
Exec subprocess
Active contributors: meowgorithm, aymanbagabas
Purpose
tea.Exec lets a Bubble Tea program hand the terminal over to a child process — typically an interactive program like $EDITOR, git rebase -i, or less. The runtime releases raw mode, runs the child, then reclaims the terminal when it exits.
API
exec.go:
// High level: pass an *exec.Cmd
cmd := tea.ExecProcess(exec.Command("vim", "file.txt"), func(err error) tea.Msg {
return EditorFinishedMsg{err: err}
})
return m, cmd
// Low level: pass any ExecCommand implementation
type ExecCommand interface {
Run() error
SetStdin(io.Reader)
SetStdout(io.Writer)
SetStderr(io.Writer)
}ExecProcess wraps an *exec.Cmd in osExecCommand, which sets stdin/stdout/stderr to the program's terminal if they are not already configured.
How it works
sequenceDiagram
participant M as Model
participant L as eventLoop
participant P as Program
participant C as Child process
M->>L: tea.ExecProcess(cmd, cb) returns Cmd
L->>P: execMsg{cmd, fn}
P->>P: releaseTerminal(false)
P->>P: SetStdin/Stdout/Stderr to TTY
P->>C: cmd.Run() (blocks event loop)
C-->>P: exit status
P->>P: RestoreTerminal()
P-->>M: cb(err) Msg
L->>M: forwarded as normal MsgThe key call chain (in tea.go's eventLoop):
case execMsg:
p.exec(msg.cmd, msg.fn)Program.exec (exec.go):
p.releaseTerminal(false)— flushes pending output, restores TTY state, cancels the input reader.- Configures the child's stdio to share the program's TTY.
c.Run()— blocks until the child exits.eventLoopis paused; signals still arrive but theirMsgs are buffered.p.RestoreTerminal()— re-enters raw mode, restarts the renderer (which replays mode flags from the lastView), reinitializes the input reader, and triggers acheckResizesince the terminal may have been resized while the child ran.- The user-supplied callback runs with the child's error and produces the
Msgthat completes the round trip.
Examples
examples/exec/main.go shows opening $EDITOR on key press. The pattern:
case tea.KeyPressMsg:
if msg.String() == "e" {
return m, tea.ExecProcess(exec.Command("vim", "tmp.txt"), editorFinishedMsg)
}Caveats
- Don't use for non-interactive I/O. A regular
Cmd(i.e. atea.Cmd) handles HTTP requests, file reads, and DB calls.Execis for things that need the TTY. - No mid-command cancellation. Once the child starts, the eventLoop pauses. Use
os/exec's context-based start for cancellable children — but you still cannot redraw the parent UI while the child is running. - Windows TTY semantics. The child must understand the same console the parent uses; Bubble Tea does not allocate a new pseudo-terminal.
- Stdin sharing. If the child reads from stdin, it shares the program's input file. Anything the user types during the child goes to the child, not the model.
Related pages
- Program runtime —
releaseTerminal/RestoreTerminalare the same primitives used by suspend/resume. - Commands —
Execis technically aCmd.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.