gosh is a compact Bash-style shell written in Go.
It uses mvdan.cc/sh/v3 for shell
parsing and interpretation, then adds the pieces that make it usable as an
interactive shell: readline editing, history, prompt rendering, tab completion,
bind, and Bash-flavored compatibility helpers.
Use it as a small local shell, a script runner, or an embeddable shell runtime inside a Go program.
Build the CLI from a checkout:
git clone https://github.com/phuslu/gosh
cd gosh/cmd/gosh
go build -mod=readonly -o gosh .
./goshRun a one-shot command:
./gosh -c 'name=gosh; for n in 1 2 3; do printf "%s:%s\n" "$name" "$n"; done'Run a script from standard input:
printf 'echo one\necho two\n' | ./goshStart an interactive session:
./gosh
sh-0.0$ echo "$BASH_VERSION"
0.0.0(1)-gosh
sh-0.0$ shopt -s extglob
sh-0.0$ shopt -q extglob && echo enabled
enabled- Bash-style syntax through
mvdan.cc/sh/v3: variables, functions, conditionals, loops, pipelines, redirects, command substitution, arithmetic, globbing, and shell builtins. - Interactive line editing with readline.
- Command and path completion.
- Prefix history search for the up/down arrow bindings.
- Bash prompt escape rendering for common
PS1andPS2sequences. bindsupport for common cursor and history-search actions.historybuiltin backed byHISTFILE,HISTSIZE, andHISTCONTROL.shopt -qcompatibility, includingbuiltin shopt -qandcommand shopt -q.BASH_VERSIONand$-compatibility variables for shell startup files.- Programmatic use through
gosh.Run.
Interactive gosh loads GOSH_ENV when it is set. If GOSH_ENV is unset, it
loads $HOME/.bashrc for interactive sessions. Non-interactive scripts from
standard input and commands run with -c do not load a startup file or render
prompts.
Example ~/.bashrc or GOSH_ENV file:
if [ -n "${BASH_VERSION-}" ] && case "$-" in *i*) true;; *) false;; esac; then
bind '"\e[1~": beginning-of-line'
bind '"\e[4~": end-of-line'
bind '"\e[5~": previous-screen'
bind '"\e[6~": next-screen'
bind '"\e[F": end-of-line'
bind '"\e[H": beginning-of-line'
bind '"\e[B": history-search-forward'
bind '"\e[A": history-search-backward'
fi
export LC_ALL=en_US.UTF-8
export TERM=xterm-256color
export SHELL=/bin/bash
export PATH="$HOME/.local/bin:$PATH"
export HISTFILE="$HOME/.gosh_history"
export HISTSIZE=1000
export HISTCONTROL=ignoreboth
export PS1='\[\e]0;\h:\w\a\]\n\[\e[1;32m\]\u@\H\[\e[0;33m\] \w \[\e[0m[\D{%T}]\n\[\e[1;$((31+3*!$?))m\]\$\[\e[0m\] '
alias ls='ls -p --color'
alias ll='ls -lF --color'
alias grep='grep --color'gosh.Run accepts explicit input, output, environment, arguments, and working
directory, so it can be used without a process-level shell.
package main
import (
"bytes"
"fmt"
"github.com/phuslu/gosh"
)
func main() {
var stdout, stderr bytes.Buffer
err := gosh.Run(gosh.Config{
Args: []string{"gosh", "-c", `printf "hello %s\n" "$1"`, "gosh", "world"},
Stdout: &stdout,
Stderr: &stderr,
Env: []string{
"PATH=/usr/bin:/bin",
"HOME=/tmp",
"HISTFILE=/dev/null",
},
})
if err != nil {
fmt.Println(stderr.String())
panic(err)
}
fmt.Print(stdout.String())
}gosh is Bash-flavored, not a byte-for-byte Bash replacement. Shell syntax and
many builtins come from mvdan.cc/sh/v3, while external commands are executed
from the host PATH.
Some Bash features that depend on a full POSIX job-control shell, a PTY-managed
process tree, or Bash internals may behave differently. Treat gosh as a small,
embeddable shell with strong Bash compatibility for common scripts and
interactive workflows, not as a login-shell replacement.
Run the library tests from the repository root:
go test ./...Build the CLI from its module:
cd cmd/gosh
go build -mod=readonly -o gosh .