Type shell pipelines. Get typed streams.
yup>is a shell whose every command is a type-safe Go function — composed in-process, one binary, no coreutils underneath.
yup> cat access.log | grep 503 | cut -d' ' -f1 | sort | uniq -c | sort -rn | head -3
3 203.0.113.7
1 198.51.100.4
1 192.168.1.10
You already know how to read that. That is the entire point.
yupsh (Yup Shell, as in bash, fish, and now yup) is an interactive shell built on gloo-foo, a Go framework in which every command is a typed stream transformer — a gloo.Command[[]byte, []byte] — that composes with every other one.
yupsh gathers those commands into a prompt. When you type seq 1 10 | grep -v 5 | head -n 3, yupsh does not fork seq, grep, and head and plumb file descriptors between three processes. It builds three Go values, snaps them together into one pipeline, and runs it inside its own process. The pipe character is real composition, not an operating-system trick.
The pleasant surprise is how little that changes for you. The names are the ones you know. The flags are the ones you know (wc -l, grep -v, cut -d , -f 2, and yes, even the old head -5 shorthand). Globs expand, ~ expands, quotes stop expansion, and up-arrow brings back what you typed. Muscle memory just works — it is a shell by consequence, not by imitation.
yup> echo hello world
hello world
yup> seq 1 10 | grep -v 5 | head -n 3
1
2
3
yup> seq 1 100 | grep 7 | wc -l
19
yup> echo HELLO WORLD | tr A-Z a-z
hello world
yup> echo hello yup | rev
puy olleh
yup> seq 1 5 | tac
5
4
3
2
1
yup> echo yup | base64
eXVw
Every stage above ran in the same process, as a typed Go command — no fork, no exec, no $PATH lookup.
One self-contained binary (about 10 MB) with the whole toolbox inside.
go install github.com/yupsh/repl/cmd/yupsh@latest
yupshIt is interactive on a terminal (history and in-line editing) and plain-and-deterministic when you pipe a script into it:
yupsh < script.yupPrefer your existing shell? Each command also ships as its own tiny CLI, backed by the very same gloo-foo command module the shell uses:
go install github.com/yupsh/yup-seq@latest
go install github.com/yupsh/yup-grep@latest
go install github.com/yupsh/yup-wc@latest
yup-seq 1 100 | yup-grep 7 | yup-wc -l
# 19Same behavior, same flags, same --help — just invoked the classic way.
Type help at the prompt for the live catalogue. Commands marked source start a pipeline; everything else filters what flows through it.
| Family | Commands |
|---|---|
| Generate | echo · emit · seq · yes (sources) |
| Filesystem | ls · find (sources) · basename · dirname |
| Search & filter | grep · comm · uniq |
| Transform | tr · sed · rev · cut · paste · split · join · nl · cat · tac · head · tail · tee · xargs |
| Summarize & inspect | wc · sort · shuf · diff · hexdump · base64 · json |
| Escape hatches | exec · git · perl |
That is 35 commands in the shell. Browse the standalone yup-* repositories for the one-command-per-binary editions.
Purity is lovely right up until you need jq or make. For those moments, three commands deliberately leave the process:
yup> echo hi | exec cat
hi
yup> echo hello | perl -p 's/l/L/g'
yup> echo x | git --version
exec runs any program on your machine, git runs your system git, and perl runs a one-liner. Everything else stays in-process.
- Tokenize. The line is split into words, remembering what was quoted, then split into stages on
|. - Expand. Unquoted
~and globs (*,?,[…]) expand against the filesystem. A glob that matches nothing stays literal, the way POSIX intends. - Translate flags. Each Unix-style flag you typed becomes the typed option the command actually takes —
wc -lbecomeswc.WcLines,head -n 10becomeshead.HeadLines(10),grep -vbecomesgrep.GrepInvert. - Choose the input. A source command generates the stream; a first command with file arguments reads those files; anything else reads standard input.
- Run. The stages are composed into one gloo-foo pipeline and executed with a context, streaming bytes from the first stage to your terminal.
Adding a command to the shell is, for most filters, a single entry in a registry map. The heavy lifting lives in the command's own gloo-foo module.
Honesty is a feature, so here is the fine print. yupsh is a pipeline shell, not a POSIX shell. Today it does not do:
- Redirection —
echo hi > out.txtprintshi > out.txtand writes no file. - Variables and substitution —
X=1is an unknown command;$HOMEand$(…)are passed through as literal text. - Command lists —
&∧are plain arguments, not separators. awkandwhile— their gloo-foo commands take Go values (an awk program structure, a Go function) that cannot yet be built from a line of text. Whenawkgrows a script parser, it gets wired up like everything else.
If you need those, your regular shell is one exit away — and the yup-* commands work happily inside it.
help · version · clear · exit / quit — and any line starting with # is a comment, so scripts can explain themselves.
gloo-foo/framework the typed-stream pipeline engine
│
gloo-foo/cmd-* one command per module: cat, grep, wc, …
│
├── yupsh/repl the interactive shell that gathers them all → yup>
└── yupsh/yup-* each command as its own standalone CLI
yupsh is where the gloo-foo commands come to be used by humans at a prompt; gloo-foo is where they come to be used by programs.
- yup.sh — the project home, with a browser playground and the command reference
- repl — the shell itself
- docs.repl — usage, architecture, the command catalogue, and development notes
- yup-* commands — the standalone CLIs
- gloo-foo — the framework and command modules underneath
yup.
gloo-foo commands, gathered into a single-binary shell.