Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.3.2 (2026-03-06)
Bash.Session.new(...)accepts new options for loading env vars from the runtime's environment. Optionenv_include: ["ENVVAR", ...]will pluck these keys fromSystem.get_env()when the session is initialized. Likewise,env_exclude: ["ENVVAR", ...]will drop the provided keys and include the rest fromSystem.get_env(). Only one of these options can be provided at a time. By default, the runtime's environment variables are not included to avoid accidentally leaking secrets.
0.3.1 (2026-03-04)
- Fixes hex.pm release. Dang
README.mdwasn't included causing a compile error. 🤦
0.3.0 (2026-01-30)
- Remove
Bash.Interop.IOand other builtin context handling to consolidate intoBash.Context. Expanded and clarified thedefbashreturn signals and how to update state. An issue with the now-deletedBash.Interop.IOwas that it would accumulate state where we intend be focused on streams.
0.2.1 (2026-01-29)
- Remove redundant
Bash.Validator - Update documentation
0.2.0 (2026-01-29)
-
Add
mix bash.formatandmix bash.validatetasks -
Improve formatter to not rely on regex
-
Support extglob
-
Support file descriptors 3+.
-
Full support for coprocs.
-
waitcommand now properly waits. -
Add
Bash.AST.Walkableprotocol andMacro-style AST traversal functions.Bash.AST.prewalk/2— top-down transformation. Visit each node before its children. Returnnilto remove a node from its parent list.import Bash.AST # Remove all `rm` commands from a parsed script safe_script = Bash.AST.prewalk(script, fn node when is_command(node, "rm") -> nil node -> node end)Bash.AST.postwalk/2— bottom-up transformation. Visit each node after its children have been processed.# Rename all commands by appending a suffix result = Bash.AST.postwalk(script, fn cmd when is_command(cmd) -> %{cmd | name: %Bash.AST.Word{parts: [{:literal, name <> "_v2"}]}} node -> node end)Bash.AST.reduce/3— fold over all nodes without modifying the tree.# Collect all command names in a script names = Bash.AST.reduce(script, [], fn node when is_command(node) -> [command_name(node) | acc] _, acc -> acc end)Bash.AST.walk_tree/4— full traversal with accumulator and separatepre/postcallbacks for maximum control.# Count total AST nodes {_script, count} = Bash.AST.walk_tree(script, 0, fn node, acc -> {node, acc + 1} end, fn node, acc -> {node, acc} end ) -
Add guard macros for concise AST pattern matching:
command_name/1,is_command/1/2,assignment_name/1, andis_assignment/1/2.These work in
whenclauses, making walker callbacks much more readable:import Bash.AST # Remove dangerous commands using a guard Bash.AST.prewalk(script, fn node when is_command(node, "rm") -> nil node when is_command(node, "sudo") -> nil node -> node end) # Collect command names using a guard Bash.AST.reduce(script, [], fn node, acc when is_command(node) -> [command_name(node) | acc] _, acc -> acc end) # Filter assignments by name Bash.AST.prewalk(script, fn node when is_assignment(node, "SECRET") -> nil node -> node end)
0.1.0 (2026-01-28)
Initial Release. A Bash interpreter written in pure Elixir. Parses and executes Bash scripts with compile-time validation, session state management, and Elixir interoperability.
Added
Core API
Bash.run/1,2,3- Execute Bash scripts with optional sessionBash.run_file/1,2,3- Execute Bash script filesBash.parse/1- Parse Bash scripts to AST at runtimeBash.parse_file/1- Parse Bash script filesBash.validate/1- Validate Bash syntax without executionBash.with_session/1,2- Manage session lifecycle with auto-cleanupBash.format/1,2- Format Bash codeBash.stdout/1,Bash.stderr/1,Bash.output/1- Extract command outputBash.exit_code/1,Bash.success?/1- Check command results
Compile-time Sigils
~BASH"script"- Parse at compile time, return AST~b"script"- String interpolation variant with Elixir variable embedding- Modifiers:
S(stdout),E(stderr),O(combined output) and common Bash set options such ase(errexit)v(verbose)p(pipefailandu` nounset to error with undefined variables. - ShellCheck-compatible error messages with line/column info
Language Constructs
- Control Flow:
if/elif/else/fi,case/esac,while,until,for, C-stylefor ((;;)) - Compound Commands:
{ ... }groups and( ... )subshells - Pipelines: Multi-stage pipes with
|, background execution with& - Logical Operators:
&&and||with short-circuit evaluation - Functions:
function name { }andname() { }syntax with local variables
Variable Expansion
- Basic expansion:
$VAR,${VAR} - Default values:
${VAR:-default},${VAR:=default},${VAR:?error},${VAR:+alternate} - String operations:
${#VAR},${VAR:offset:length} - Pattern removal:
${VAR#pattern},${VAR##pattern},${VAR%pattern},${VAR%%pattern} - Substitution:
${VAR/pattern/replacement},${VAR//pattern/replacement} - Case modification:
${VAR^},${VAR^^},${VAR,},${VAR,,} - Prefix expansion:
${!prefix*},${!prefix@} - Parameter transforms:
${VAR@Q},${VAR@a},${VAR@E},${VAR@A},${VAR@u},${VAR@L} - Indirect expansion:
${!VAR}
Arrays
- Indexed arrays:
arr=(a b c),arr[0]=value - Associative arrays via
declare -A - Array access:
${arr[0]},${arr[@]},${arr[*]} - Array length:
${#arr[@]} - Array key listing:
${!arr[@]}
Arithmetic
- Arithmetic expansion:
$((expression)) - Arithmetic command:
(( expression )) - Full operator support:
+,-,*,/,%,** - Bitwise operators:
&,|,^,~,<<,>> - Comparison:
<,>,<=,>=,==,!= - Logical:
&&,||,! - Ternary:
? : - Increment/decrement:
++x,x++,--x,x-- - Assignment operators:
=,+=,-=,*=,/=,%=, etc. - Base conversion:
16#FF,8#77,2#1010(bases 2-64)
Command Substitution
- Modern syntax:
$(command) - Nested substitution support
- Process substitution:
<(command),>(command)
Brace Expansion
- List expansion:
{a,b,c} - Sequences:
{1..10},{a..z} - Step sequences:
{1..10..2} - Zero-padding:
{01..10} - Nested braces:
{a,b{1,2}}
Quoting
- Single quotes (literal)
- Double quotes (with expansion)
- ANSI-C quoting:
$'...' - Escape sequences
Redirections
- Input/output:
<,>,>> - File descriptor duplication:
2>&1,>&2 - Combined:
&>,&>> - Heredoc:
<<,<<-(with tab stripping) - Herestring:
<<<
Test Expressions
test/[ ... ]- POSIX test[[ ... ]]- Extended test with:- File tests:
-f,-d,-e,-r,-w,-x,-s,-L, etc. - String tests:
-z,-n,=,!=,<,> - Numeric tests:
-eq,-ne,-lt,-le,-gt,-ge - Variable tests:
-v(isset),-R(nameref) - Pattern matching:
==,!=with glob patterns - Regex matching:
=~withBASH_REMATCHcapture
- File tests:
Builtin Commands (53 total)
I/O
echo- Output with-n,-eflagsprintf- Formatted output with-vvariable assignmentread- Input with-r,-p,-t,-n,-d,-a,-soptions
Variables
export,declare,local,readonly,unset- Full attribute support:
-a,-A,-i,-r,-x,-n,-u,-l
Directory
cd- With-L,-Pflags andCDPATHsupportpwd,pushd,popd,dirs
Control Flow
break,continue,return,exit
Job Control
jobs,bg,fg,wait,disown,kill
Shell Options
set- Shell options (-e,-u,-x,-a,-o pipefail, etc.)shopt- Extended options (dotglob,extglob,nullglob, etc.)
Execution
source/.- Execute in current shelleval- Parse and execute stringexec- Replace shell processcommand,builtin- Bypass aliases/functions
Other
type,hash,help,true,false,:,alias,unaliastrap- Signal handlers for EXIT, DEBUG, RETURN, signalsgetopts- Option parsingmapfile/readarray- Read lines to arraylet- Arithmetic evaluationtimes,ulimit,umask,caller,enable,suspendcomplete,history,fccoproc- Coprocess management
Session Management
- Persistent environment across commands
- Working directory tracking
- Command history
- Function and alias storage
- Job table for background processes
- Directory stack
- Shell options state
Elixir Interop
defbashmacro for Elixir functions callable from Bash- Namespace-based function calls:
namespace.function args - State access and modification from Elixir
- Multiple return value formats (
:ok,{:ok, msg},{:error, msg})
Special Variables
$?- Last exit status$$- Shell PID$!- Last background PID$0,$1-$9,$@,$*,$#- Positional parametersPIPESTATUS- Pipeline exit codesBASH_REMATCH- Regex capturesRANDOM,LINENO,SECONDS,EPOCHSECONDS,EPOCHREALTIMEBASH_VERSION,HOME,PWD,OLDPWD,PATH,IFS
Error Handling
set -e/errexit- Exit on errorset -u/nounset- Error on unbound variablesset -o pipefail- Pipeline failure propagation- ShellCheck-compatible error codes and messages
Architecture
- Recursive descent parser following Bash grammar
- Token-based lexer with proper quoting/escaping
- AST-based execution model
- GenServer-based session management
- Supervisor hierarchy for job and process management
- Asynchronous output collection with streaming support