Repository navigation
feat: Bun shell - #7748
Merged
Merged
feat: Bun shell#7748
Conversation
Collaborator
|
🎉 |
This was referenced Sep 13, 2026
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
This adds a cross-platform bash-like shell scripting language to Bun. Scripts are runnable through a zx inspired API that allows for embedding of JS expressions into the shell command (similar to embedding JS expressions in JSX):
Bun shell scripts will also be runnable in package.json scripts, or through
bun run, and files with the appropriate shebang.Shell features
cmd1 | cmd2cmd1 && cmd2cmd > lmao.txt)foo 1> lmao.txtorfoo 2> lmao.txt)FOO=barecho $FOOecho $(cmd with args))echo {a,b,c}{d,e,f}echo {a,b,{c,d}}{e,f}${someBuffer} | cmd1 | cmd2)bun runSupported builtin commands
export: set environment variables for the shell session to be used by subprocessescd: change the current working directory for the shellcd <arg>cd -(go to previous directory)echo: write text to stdoutpwd: print previous directorywhich: print absolute paths of stuffrm: delete stuff-v: verbose mode, prints out what is deleted-i: interactive mode, ask user for confirmationmv: move/rename stuffls: list stuff in directories-a/-A: show all or show everything except.and..Other stuff todo
.protect()ed and.unprotect()ed, but this is slow need a better way to do itReviewing notes
Lexer/parser
The
src/shell.zigfile contains the lexer and parser for the shell language. This file also contains the v1 blocking and synchronous interpreter which isn't really used right now but might be used for a sync API.Shell subprocess
The
src/shell/subproc.zigfile contains a modified version of theSubprocessstruct for the shell. Originally, it was supposed to share some code with the originalSubprocess, (which was insidesrc/shell/util.zig) but it became a bit too complicated to abstract it for both because they had different requirements so now they are separate things andsrc/shell/util.zigshould probable be deleted.Interpreter
The
src/shell/interpreter.zigfile contains the interpreter rewritten to be non-blocking. It also contains the implementation for all the builtin shell commands.The interpreter works by walking the AST and creating state machine structs for each node.
For example, for something like
cmd1 && cmd2it would create a state machine for the binary expression, storing some state like whether its executing the left hand side or the right hand side. It will also create state machines for its children,cmd1andcmd2which need their own state for all the execution state of managing the subprocess.When we need to do work that might block like writing to a file descriptor, we schedule the poll and stop doing work in the interpreter, basically suspending execution. Then when the poll is ready it will call a function on whichever state machine it was associated with and resume execution
Memory management
The lexer/parser allocate tokens/ast nodes into an arena. The AST and arena are both handed to the Interpreter.
The interpreter has to heap allocate state machines, so it uses an allocator different from the AST arena to do that.
Builtin commands, when executed, need to store their
argvand some environment variables, those are allocated into an arena. The builtin has access to that arena for the duration of its execution if it wants to use it for quick scratch allocations. For example, when the builtin fails it needs to write the error message to stderr so it might use the arena to allocate a formatted string