Skip to main content

Bash_PurePython

PurePython implementations of common bash commands, for use with pyodide.

These are mostly vibe coded, use at your own risk.

Running scripts

bash_purepython.execute_command runs bash-like scripts on asyncio, with no subprocesses or threads, which is what pyodide needs. A script is split into top-level blocks, each block is turned into a plan, the plan is executed, and any script text found along the way (loop bodies, function bodies, $( ) substitutions, subshells) re-enters the same loop.

Under pyodide, where an event loop is already running, await the entry point (for example from runPythonAsync with top-level await):

from bash_purepython.execute_command import run_script_async
from bash_purepython.shell_state import ShellState

state = ShellState.for_terminal()
exit_code = await run_script_async("yes | head -n 3; echo $(echo sub) | cat", state)
print(state.stdout, state.stderr, exit_code)

On CPython, run_script(script, state) drives its own loop, and ShellRunner keeps one loop alive across calls so background jobs survive between them.

Pipelines stream lazily: a command declares the kinds of input and output it supports (Iterator[str], str, Iterator[bytes], bytes, synchronous or asynchronous) and the engine negotiates a shared kind between neighbouring stages, so yes | head runs yes only as far as head reads. A group, loop, conditional or function used as a pipeline stage runs as its own task and streams through a bounded queue, so ( yes ) | head -n 2 terminates too. Standard output and standard error are written to the state's sinks as each chunk is produced and are also recorded on the state. A sink may be an async callable, such as a JavaScript function, and is awaited per chunk.

cmd & starts a background job on the event loop. $! holds its pid, the fake ps lists the job table (R running, D done, K killed), kill PID cancels a job, wait [PID] waits and reaps, and jobs lists them bash-style.

Register extra commands by subclassing bash_purepython.command.command.Command and adding an instance to a CommandRegistry. A command's run may be a plain function or a coroutine, so it can await a browser promise such as fetch.

Supported so far: pipelines (|, |&, !), &&/|| lists, if/elif/else, for (over words and for (( ; ; ))), while, until, case, functions, subshells, brace groups, background jobs, redirections on commands and on compound commands (>, >>, <, 2>, 2>>, 2>&1, >&2, &>, >&-, heredocs, here-strings, /dev/null), parameter and command substitution with the ${x#}, ${x%}, ${x/}, ${x^^}, ${!x} and ${x:offset:length} operators, indexed arrays, brace expansion, ANSI-C quoting ($'...'), IFS word splitting, arithmetic ($(( )) and the (( )) command), the [[ ]] conditional command with BASH_REMATCH, pathname expansion (*, ?, [...]), aliases, and the builtins cd, pushd, popd, dirs, export, unset, exit, return, break, continue, wait, jobs, read, shift, set (-e, -u, -x, -o pipefail, --), local, eval, type, command, alias, unalias, let, declare, typeset, readonly, hash, xargs. Script files run with bash file, sh file, bash -c text, a script on standard input, or by a path such as ./file, each against a copy of the state; source file and . file run one against the live state. Commands shipped: [, base64, basename, cat, chmod, column, cp, cut, date, df, diff, dirname, du, echo, env, expr, false, file, find, fold, grep, gunzip, gzip, head, help, hostname, id, kill, less, ls, md5sum, mkdir, mktemp, more, mv, nl, od, paste, printf, ps, pwd, realpath, rev, rm, rmdir, sed, seq, sha1sum, sha256sum, sha512sum, sleep, sort, stat, tail, tar, tee, test, touch, tr, tree, true, uname, uniq, unzip, wc, which, whoami, xxd, yes, zip. Every command takes --help; -h is left free for the commands that use it, such as ls -h and grep -h. help lists them all with a summary.

select prints its menu and PS3 prompt to standard error and reads the choice from standard input, laid out in columns the way bash does. Process substitution <( ) and >( ) hands the command a temporary file in place of a pipe: an input substitution is filled before the command runs, an output substitution is fed to its script once the command's block has finished, and the file is removed afterwards.

Known limitations: exec, trap and getopts are not implemented; >&2 followed by a later 2> redirection is resolved in order, but a |& pipe combined with >&2 is not; a $( ) substitution that never stops writing, such as echo "$(yes)", is stopped at the executor's output limit (16 MiB by default) and fails the command with an error, where real bash would hang.

bash_purepython.continuation.script_needs_more_lines tells an interactive host whether the lines typed so far still leave a quote, heredoc, group or compound command open, so it can prompt for another line.

Example terminal

examples/terminal.py is a small interactive terminal built on the engine, showing what a host supplies: one ShellState for the session, one ShellRunner so background jobs survive between lines, a prompt, and a continuation prompt while a quote or here-document is open. Run it with:

uv run python -m examples.terminal

For a browser host, bash_purepython.pyodide_session.PyodideSession plays the same role under Pyodide and adds commands the page implements itself.

Testing

uv run pytest runs the in-memory suite. tests/test_live_bash.py runs the same several hundred scripts through a real bash binary and the engine and compares standard output and exit codes; it is marked live_bash_test and excluded by default. Run it with:

uv run pytest -m live_bash_test

Metadata

Release files for bash-purepython 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bash-purepython 0.5.0
File Size Uploaded
bash_purepython-0.5.0.tar.gz 222.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bash-purepython 0.5.0
File Interpreter ABI Platform
bash_purepython-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 450.1 kB

Release files / bash_purepython-0.5.0.tar.gz

Download URL bash_purepython-0.5.0.tar.gz
Size 222.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c74f1cbf360691253e42800c705baf27d343ec602d04ecf3e4f62811254f6843
BLAKE2b-256 checksum
How to use checksums
d54d57ac302ab4f1a588a5f5aacd53a7ab242411db186c8284e20d21711bc481
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / bash_purepython-0.5.0-py3-none-any.whl

Download URL bash_purepython-0.5.0-py3-none-any.whl
Size 227.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
96b169707ac4e665efd8539402f000572481295900ae386ac633a2fae9e02ca9
BLAKE2b-256 checksum
How to use checksums
b54a68415008dc930fc5319f620d2a97e1bc578a68eccc1480547c293a934199
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page