A bash to C transpiler
Project description
No tears. Only toolchains.
shellraiser:
The bash script compiler no one asked for.
Compile Bash scripts to native binaries. The compiled output runs 5-12x faster on computation-heavy workloads, integrates seamlessly with the rest of your shell environment, and can export compiled functions and variables back into plain Bash sessions.
In other words, if you want to use the wrong tool (bash) for the wrong job (a computationally heavy one), shellraiser lets you do the wrong thing fast.
Primary legitimate usage case: speed up some of your bash completions.
How It Works
To put it pedantically, shellraiser is actually a bash-to-c transpiler. It takes your bash script and turns it into C code that, along with the included shellraiser C runtime, can be compiled by your installed C compiler into a native machine code binary without any runtime dependencies.
However, shellraiser largely automates the compilation process (assuming you have a natively supported C compiler available, such as gcc or clang), so from a usage standpoint, you just give it bash scripts and shellraiser
gives you compiled executable binaries.
Quick Start
./shellraiser script.sh # -> ./script (native binary)
./shellraiser script.sh -o mybin # custom output name
./shellraiser script.sh --sourceable # polyglot: source OR execute (see below)
./shellraiser script.sh --emit-only # just print the generated C
Requires Python 3.8+, GCC or Clang, Linux or macOS. The output binary is self-contained with no runtime dependencies.
Key Features
Compiled Functions Callable From Any Shell
When a compiled script calls export -f, the function becomes a real command on $PATH. Any shell, script, or tool can call it by name and it runs as native compiled code, not interpreted Bash.
# utils.sh -- compile with shellraiser
to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
export -f to_upper
# Anywhere else -- plain Bash, xargs, find, parallel:
to_upper "hello" # -> HELLO
echo -e "foo\nbar" | xargs -I {} bash -c 'to_upper "{}"'
find . -name "*.txt" -exec to_upper {} \;
Under the hood, export -f creates a shell shim on $PATH that routes back into the compiled binary via --call. The calling code doesn't know the function is compiled. Cleanup is automatic on exit.
This also works in reverse: if a parent Bash session exports a function with export -f, the compiled binary can call it. The runtime detects BASH_FUNC_name%% environment variables and dispatches through Bash.
Source Compiled Scripts Into Your Shell
With --sourceable, the output is a single file that can be both executed and sourced:
./shellraiser mylib.sh -o mylib --sourceable
./mylib # run as a compiled binary
source ./mylib # load its functions + variables into your shell
After sourcing, all functions and variables defined in the compiled script are available in your current session. Functions call back into the cached binary, running as native code.
source ./mylib
echo "$VERSION" # variable from the compiled script
process_data "input" # compiled function, runs as native code
type process_data # -> "process_data is a function"
The binary is embedded as a base64 payload, extracted and cached at ~/.cache/shellraiser/ on first run. Subsequent runs skip extraction.
Background Pipelines With Process Monitoring
Multi-command pipelines can be backgrounded with proper $! tracking. The common pattern of parallel work with a progress-monitoring loop works correctly:
printf "%s\n" "${items[@]}" | xargs -P 4 -I {} bash -c 'do_work "{}"' &
pid=$!
while ps -p $pid > /dev/null 2>&1; do
count=$(find "$tmpdir" -name "*.done" | wc -l)
printf "\rProgress: %d/%d" "$count" "$total"
sleep 0.1
done
The runtime handles backgrounded pipelines by forking a supervisor process, correctly sets $!, reaps zombie processes between commands (so ps -p detects completion), and guards EXIT traps against firing in child processes.
Associative Arrays
Full declare -A support backed by a real hash table:
declare -A config
config["host"]="example.com"
config["port"]="8080"
for key in "${!config[@]}"; do
echo "$key = ${config[$key]}"
done
echo "entries: ${#config[@]}"
Everything Else You'd Expect
- Indexed arrays with append (
+=), expansion (${arr[@]}), length (${#arr[@]}), and command-substitution init (arr=($(cmd))) - Functions with
localscoping,return, positional params,shift, recursion - All control flow:
if/elif/else,while,until,for-in, C-stylefor((;;)),case/esac,break,continue,&&/||chains, subshells - Pipes and redirects:
|,>,>>,<,2>,2>&1,>&2 - 28 builtins including
echo -e,printf(with format repetition,-v, width specs),[[ ]](with=~,&&,||),declare,trap,kill,command -v - Special variables:
$?,$$,$!,$#,$@,$*,$0-$9 - Extended test:
[[ ]]with regex matching (=~), pattern globbing (==), logical operators - ANSI-C quoting:
$'\n',$'\t',$'\xNN' - String append:
var+="text"(in-place, O(1) amortized) - Glob expansion:
*,?,[...]in command arguments - Trap handling:
trap 'cmd' EXITand signal traps (INT, TERM, HUP, etc.)
Performance
bash bench.sh 100000
Typical results at N=100000 (macOS, x86-64):
Test Bash Compiled Speedup
---- ---- -------- -------
Arithmetic (100000 iterations) 1176ms 130ms 9.0x
String append (100000 iterations) 2138ms 1327ms 1.6x
Function calls (100000 calls) 1638ms 161ms 10.1x
Array append (100000 ops) 1107ms 97ms 11.4x
Assoc array set (20000 ops) 294ms 56ms 5.2x
Assoc array get (20000 ops) 346ms 60ms 5.7x
Conditionals (100000 branches) 1670ms 159ms 10.5x
Nested loops (100x1000) 1240ms 164ms 7.5x
echo to /dev/null (100000 calls) 7356ms 5415ms 1.3x
printf to /dev/null (100000 calls) 7330ms 5090ms 1.4x
[[ ]] extended test (100000 evals) 1206ms 122ms 9.8x
C-style for loop (100000 iters) 782ms 158ms 4.9x
case/esac (100000 matches) 1259ms 130ms 9.6x
Subshell fork (1000 forks) 1960ms 1652ms 1.1x
Bash Language Feature Support
| Variables & Expansion | Status | Notes |
|---|---|---|
var=value, $var, ${var} |
✅ | |
| Single, double, backslash quoting | ✅ | |
$'...' ANSI-C quoting (\n, \t, \xNN, \0NNN) |
✅ | |
$(( arithmetic )) — all C operators |
✅ | |
$(command) substitution |
✅ | |
`backtick` substitution |
✅ | |
var+="append" |
✅ | |
${#var} string length |
✅ | |
${var:-default} default value |
❌ | |
${var:=word}, ${var:+word}, ${var:?msg} |
❌ | |
${var:offset:length} substring |
❌ | |
${var//pat/rep} substitution |
❌ | |
${var#pat}, ${var##pat} prefix strip |
❌ | |
${var%pat}, ${var%%pat} suffix strip |
❌ | |
${var^^}, ${var,,} case conversion |
❌ | |
| Control Flow | ||
if / elif / else / fi |
✅ | |
while, until |
✅ | |
for var in items (with word splitting) |
✅ | |
for (( init; cond; step )) |
✅ | |
case / esac (with *, ?, | patterns) |
✅ | |
break, continue |
✅ | |
&&, || chains |
✅ | |
Subshells ( ... ) |
✅ | |
Brace groups { ...; } |
✅ | |
select |
❌ | |
coproc |
❌ | |
| Functions | ||
name() { body; } and function name { body; } |
✅ | |
local, return, shift |
✅ | |
$1–$9, $@, $*, $# |
✅ | |
export -f (compiled function export via PATH) |
✅ | |
Calling Bash-exported functions (BASH_FUNC_%%) |
✅ | |
--sourceable polyglot (source or execute) |
✅ | |
| Recursive functions | ✅ | |
| Arrays | ||
arr=(a b c), arr[i]=val |
✅ | |
${arr[i]}, ${arr[@]}, ${arr[*]} |
✅ | |
${#arr[@]} element count |
✅ | |
arr+=(val) append |
✅ | |
arr=($(cmd)) from command substitution |
✅ | |
declare -A map (associative) |
✅ | |
map["key"]="val", ${map[$key]} |
✅ | |
${!map[@]} key iteration |
✅ | |
${#map[@]} entry count |
✅ | |
declare -A map=([k]=v ...) inline init |
❌ | |
| Pipes, Redirection & Jobs | ||
cmd1 | cmd2 | cmd3 |
✅ | |
>, >>, < |
✅ | |
2>, 2>>, 2>&1, >&2 |
✅ | |
cmd & (background) |
✅ | |
cmd1 | cmd2 & (background pipeline) |
✅ | |
$! (last background PID) |
✅ | |
$$, $? |
✅ | |
| Here-strings | ✅ | Only <<< and only inside $() |
Here-documents <<EOF |
❌ | |
Process substitution <(cmd), >(cmd) |
❌ | |
Arbitrary fd numbers 3>, exec 3> |
❌ | |
| Builtins | ||
echo |
✅ | -n, -e, -E, full escape sequences |
printf |
✅ | Format repetition, -v, %b, %s, %d, %f, %x, width/precision |
cd |
✅ | |
exit |
✅ | |
read |
✅ | -r, -p |
export |
✅ | -f for function export |
test / [ |
✅ | File, string, arithmetic tests |
[[ |
✅ | =~ regex, == glob, &&, ||, !, ( ) |
true / false |
✅ | |
return |
✅ | |
local |
✅ | -a for arrays |
shift |
✅ | |
wait |
✅ | Optional PID |
unset |
✅ | |
declare / typeset |
✅ | -a, -A, -i, -x, -g, -r |
trap |
✅ | EXIT + signals; PID-guarded against child forks |
kill |
✅ | Signal names and numbers |
command |
✅ | -v for path lookup |
: (noop) |
✅ | |
source / . |
❌ | Use --sourceable instead |
eval |
❌ | |
set / shopt |
❌ | |
getopts |
❌ | |
mapfile / readarray |
❌ | |
pushd / popd |
❌ | |
| Special Variables | ||
$0, $1–$9, ${10}+ |
✅ | |
$@, $*, $# |
✅ | |
$? (exit code), $$ (PID), $! (bg PID) |
✅ | |
$IFS |
✅ | |
$RANDOM, $LINENO, $FUNCNAME, $BASH_SOURCE |
❌ | |
$PIPESTATUS, $BASH_REMATCH |
❌ |
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shellraiser-1.0.0.tar.gz.
File metadata
- Download URL: shellraiser-1.0.0.tar.gz
- Upload date:
- Size: 49.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60c318d0d7dc8cc177772b91eee12e3560b06116e684a974b038c64bc9705d39
|
|
| MD5 |
2f2a9a5b5bb5465a402651e8dcd7f8ba
|
|
| BLAKE2b-256 |
91db28b888457baca540f2fb22b660171eb07e83303d6b9cebb8a6f854c0a1e8
|
Provenance
The following attestation bundles were made for shellraiser-1.0.0.tar.gz:
Publisher:
publish.yml on metacollin/shellraiser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shellraiser-1.0.0.tar.gz -
Subject digest:
60c318d0d7dc8cc177772b91eee12e3560b06116e684a974b038c64bc9705d39 - Sigstore transparency entry: 1848372295
- Sigstore integration time:
-
Permalink:
metacollin/shellraiser@b4918207b585d075aceb1553ce2b57a2a06275c6 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/metacollin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b4918207b585d075aceb1553ce2b57a2a06275c6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file shellraiser-1.0.0-py3-none-any.whl.
File metadata
- Download URL: shellraiser-1.0.0-py3-none-any.whl
- Upload date:
- Size: 51.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a6b1eab36d5274c299f46b82c17027282c434728ea55179412267ad0a91c016
|
|
| MD5 |
13c1c04b9a38a3397b1abf6617d1818d
|
|
| BLAKE2b-256 |
998049c965adb112ba844485668c2974262c7fb364d2acc245874c36459effbd
|
Provenance
The following attestation bundles were made for shellraiser-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on metacollin/shellraiser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shellraiser-1.0.0-py3-none-any.whl -
Subject digest:
7a6b1eab36d5274c299f46b82c17027282c434728ea55179412267ad0a91c016 - Sigstore transparency entry: 1848372358
- Sigstore integration time:
-
Permalink:
metacollin/shellraiser@b4918207b585d075aceb1553ce2b57a2a06275c6 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/metacollin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b4918207b585d075aceb1553ce2b57a2a06275c6 -
Trigger Event:
push
-
Statement type: