diff --git a/compare_bash.exs b/compare_bash.exs deleted file mode 100644 index aff6e1e..0000000 --- a/compare_bash.exs +++ /dev/null @@ -1,207 +0,0 @@ -# Script to compare JustBash vs real bash -# Run with: mix run compare_bash.exs - -defmodule Compare do - def run_bash(cmd) do - {output, exit_code} = System.cmd("bash", ["-c", cmd], stderr_to_stdout: true) - {output, exit_code} - end - - def run_just(cmd) do - bash = JustBash.new() - {result, _} = JustBash.exec(bash, cmd) - {result.stdout <> result.stderr, result.exit_code} - end - - def compare(cmd) do - {bash_out, bash_exit} = run_bash(cmd) - {just_out, just_exit} = run_just(cmd) - - if bash_out == just_out and bash_exit == just_exit do - :match - else - {:diff, bash_out, bash_exit, just_out, just_exit} - end - end -end - -commands = [ - # echo variations - "echo hello", - "echo -n hello", - "echo -e 'a\\tb'", - "echo -e 'a\\nb'", - - # printf - "printf '%s\\n' hello", - "printf '%d\\n' 42", - "printf '%.2f\\n' 3.14159", - "printf '%05d\\n' 42", - - # Variable expansion - "x=hello; echo $x", - "x=hello; echo ${x}world", - "x=hello; echo ${#x}", - "echo ${undefined:-default}", - "x=set; echo ${x:+replacement}", - "x=hello; echo ${x:1:3}", - "x=file.txt; echo ${x%.txt}", - "x=file.txt; echo ${x##*.}", - - # Arithmetic - "echo $((1+2))", - "echo $((10/3))", - "echo $((10%3))", - "echo $((2**8))", - "x=5; echo $((x*2))", - - # Brace expansion - "echo {a,b,c}", - "echo {1..5}", - "echo {a..e}", - "echo pre{1,2}post", - - # Quoting - "echo \"hello world\"", - "x=test; echo \"$x\"", - - # Command substitution - "echo $(echo hello)", - "x=$(echo world); echo $x", - - # Pipes - "echo hello | cat", - "echo -e 'b\\na\\nc' | sort", - "echo hello | tr a-z A-Z", - - # tr - "echo hello | tr l L", - "echo hello | tr -d l", - "echo HELLO | tr A-Z a-z", - - # grep - "echo -e 'foo\\nbar\\nbaz' | grep bar", - "echo -e 'foo\\nbar\\nbaz' | grep -v bar", - "echo -e 'foo\\nbar\\nbaz' | grep -n bar", - "echo -e 'FOO\\nfoo' | grep -i foo", - - # sed - "echo hello | sed 's/l/L/'", - "echo hello | sed 's/l/L/g'", - "echo -e 'a\\nb\\nc' | sed -n '2p'", - "echo -e 'a\\nb\\nc' | sed '1d'", - - # awk - "echo 'a b c' | awk '{print $2}'", - "echo 'a,b,c' | awk -F, '{print $2}'", - "echo -e '1\\n2\\n3' | awk '{sum+=$1} END {print sum}'", - - # head/tail - "echo -e '1\\n2\\n3\\n4\\n5' | head -n 2", - "echo -e '1\\n2\\n3\\n4\\n5' | tail -n 2", - - # wc - "echo hello | wc -c", - "echo -e 'a\\nb\\nc' | wc -l", - - # cut - "echo 'a,b,c' | cut -d, -f2", - "echo hello | cut -c1-3", - - # uniq - "echo -e 'a\\na\\nb\\nb\\nc' | uniq", - "echo -e 'a\\na\\nb' | uniq -c", - - # sort - "echo -e 'c\\na\\nb' | sort", - "echo -e 'c\\na\\nb' | sort -r", - "echo -e '10\\n2\\n1' | sort -n", - - # Control flow - "if true; then echo yes; fi", - "if false; then echo yes; else echo no; fi", - "for i in 1 2 3; do echo $i; done", - "x=3; while [ $x -gt 0 ]; do echo $x; x=$((x-1)); done", - - # test/[ - "[ 1 -eq 1 ] && echo equal", - "[ 1 -lt 2 ] && echo less", - "[ -n 'hello' ] && echo nonempty", - "[ -z '' ] && echo empty", - - # Logical operators - "true && echo yes", - "false || echo fallback", - "true && false || echo recovered", - - # seq - "seq 5", - "seq 2 5", - "seq 1 2 10", - - # rev - "echo hello | rev", - - # basename/dirname - "basename /path/to/file.txt", - "dirname /path/to/file.txt", - - # xargs - "echo 'a b c' | xargs -n1 echo", - - # tee (just output, ignore file) - "echo hello | tee /dev/null", - - # cat with heredoc - "cat << 'EOF'\nhello\nworld\nEOF", - - # read - "echo hello | { read x; echo got $x; }", - - # Multiple statements - "echo a; echo b; echo c", - "x=1; y=2; echo $((x+y))", - - # Subshell - "(echo sub; echo shell)", - "(x=local; echo $x); echo ${x:-unset}", - - # Exit codes - "true; echo $?", - "false; echo $?", - - # Special variables - # "echo $$", # PID - will always differ between bash and JustBash - - # Arrays (if supported) - # "arr=(a b c); echo ${arr[1]}", - - # jq (if available in both) - "echo '{\"a\":1}' | jq '.a'", - "echo '[1,2,3]' | jq '.[]'", - "echo '{\"x\":\"hello\"}' | jq -r '.x'" -] - -results = - Enum.map(commands, fn cmd -> - {cmd, Compare.compare(cmd)} - end) - -matches = Enum.count(results, fn {_, r} -> r == :match end) -diffs = Enum.filter(results, fn {_, r} -> r != :match end) - -IO.puts("=== COMPARISON RESULTS ===") -IO.puts("Matches: #{matches}/#{length(commands)}") -IO.puts("") - -if diffs != [] do - IO.puts("=== DIFFERENCES (#{length(diffs)}) ===") - IO.puts("") - - Enum.each(diffs, fn {cmd, {:diff, bash_out, bash_exit, just_out, just_exit}} -> - IO.puts("Command: #{cmd}") - IO.puts(" Bash: #{inspect(bash_out)} (exit #{bash_exit})") - IO.puts(" JustBash: #{inspect(just_out)} (exit #{just_exit})") - IO.puts("") - end) -end diff --git a/demo.exs b/demo.exs deleted file mode 100644 index 43f0495..0000000 --- a/demo.exs +++ /dev/null @@ -1,176 +0,0 @@ -IO.puts("\n" <> String.duplicate("=", 60)) -IO.puts(" JustBash - A Sandboxed Bash Environment for Elixir") -IO.puts(String.duplicate("=", 60)) - -bash = - JustBash.new( - files: %{ - "/data/users.csv" => - "name,role,salary\nalice,engineer,95000\nbob,manager,120000\ncharlie,engineer,85000\ndiana,designer,90000\neve,engineer,100000", - "/data/logs.txt" => - "2024-01-15 ERROR Connection failed\n2024-01-15 INFO Server started\n2024-01-16 WARN High memory usage\n2024-01-16 ERROR Disk full\n2024-01-17 INFO Backup complete", - "/data/numbers.txt" => "42\n17\n99\n3\n256\n8\n1024" - } - ) - -run = fn bash, cmd -> - IO.puts("\n $ #{cmd}") - {result, bash} = JustBash.exec(bash, cmd) - - if result.stdout != "" do - result.stdout - |> String.split("\n") - |> Enum.reject(&(&1 == "")) - |> Enum.each(&IO.puts(" #{&1}")) - end - - if result.stderr != "", do: IO.puts(" stderr: #{result.stderr}") - bash -end - -IO.puts("\n\n[1] BRACE EXPANSION - Generate multiple items from patterns") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "echo {a,b,c}") -bash = run.(bash, "echo {1..5}") -bash = run.(bash, "echo {a..f}") -bash = run.(bash, "echo file{1,2,3}.txt") -bash = run.(bash, "echo {mon,tues,wednes,thurs,fri}day") -bash = run.(bash, "echo {1..3}{a,b}") - -IO.puts("\n\n[2] NESTED ARITHMETIC - Complex math with proper nesting") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "echo \"Nested: \\$((1 + (2 * (3 + 4))))\"") -bash = run.(bash, "echo \"Powers: \\$((2 ** 10))\"") -bash = run.(bash, "echo \"Modulo: \\$((17 % 5))\"") -bash = run.(bash, "echo \"Ternary: \\$((10 > 5 ? 100 : 0))\"") - -IO.puts("\n\n[3] NESTED PARAMETER EXPANSION - Defaults within defaults") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "echo \"Fallback: \\${X:-\\${Y:-final}}\"") -bash = run.(bash, "text=hello; echo \"Upper: \\${text^^}\"") -bash = run.(bash, "path=/usr/local/bin/script.sh; echo \"Base: \\${path##*/}\"") -bash = run.(bash, "file=doc.tar.gz; echo \"Stem: \\${file%.*}\"") -bash = run.(bash, "str=hello; echo \"Slice: \\${str:1:3}\"") - -IO.puts("\n\n[4] GLOB EXPANSION - Pattern matching") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "ls /data") -bash = run.(bash, "echo /data/*.txt") -bash = run.(bash, "echo /data/*") - -IO.puts("\n\n[5] PIPELINES & TEXT PROCESSING") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "cat /data/users.csv | head -1") -bash = run.(bash, "grep ERROR /data/logs.txt | wc -l") -bash = run.(bash, "cat /data/numbers.txt | sort -n | tail -3") -bash = run.(bash, "echo 'hello world' | tr 'a-z' 'A-Z'") - -IO.puts("\n\n[6] SED - Stream editing") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "echo 'hello world' | sed 's/world/universe/'") -bash = run.(bash, "echo 'aaa bbb ccc' | sed 's/b/X/g'") - -IO.puts("\n\n[7] CONDITIONALS & TESTS") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "test -f /data/users.csv && echo 'users.csv exists'") -bash = run.(bash, "test 10 -gt 5 && echo '10 > 5'") -bash = run.(bash, "[ -d /data ] && echo '/data is a directory'") - -IO.puts("\n\n[8] LOOPS - For with brace expansion") -IO.puts(String.duplicate("-", 60)) - -{result, bash} = - JustBash.exec(bash, """ - for fruit in apple banana cherry; do - echo "I like $fruit" - done - """) - -IO.puts("\n $ for fruit in apple banana cherry; do echo ...; done") -String.split(result.stdout, "\n") |> Enum.reject(&(&1 == "")) |> Enum.each(&IO.puts(" #{&1}")) - -{result, bash} = - JustBash.exec(bash, """ - sum=0 - for n in $(cat /data/numbers.txt); do - sum=$((sum + n)) - done - echo "Sum of all numbers: $sum" - """) - -IO.puts("\n $ # Sum all numbers from file") -String.split(result.stdout, "\n") |> Enum.reject(&(&1 == "")) |> Enum.each(&IO.puts(" #{&1}")) - -IO.puts("\n\n[9] CASE STATEMENTS - Pattern matching") -IO.puts(String.duplicate("-", 60)) - -{result, bash} = - JustBash.exec(bash, """ - for file in data.txt users.csv run.sh image.png; do - case "$file" in - *.txt) echo "$file -> text" ;; - *.csv) echo "$file -> csv" ;; - *.sh) echo "$file -> script" ;; - *) echo "$file -> unknown" ;; - esac - done - """) - -IO.puts("\n $ case \"\\$file\" in *.txt) ... ;; esac") -String.split(result.stdout, "\n") |> Enum.reject(&(&1 == "")) |> Enum.each(&IO.puts(" #{&1}")) - -IO.puts("\n\n[10] HEREDOCS - Multi-line strings with expansion") -IO.puts(String.duplicate("-", 60)) - -{result, bash} = - JustBash.exec(bash, """ - name="World" - count=42 - cat < Enum.reject(&(&1 == "")) |> Enum.each(&IO.puts(" #{&1}")) - -IO.puts("\n\n[11] XARGS - Build and execute commands") -IO.puts(String.duplicate("-", 60)) - -bash = run.(bash, "echo -e 'one\\ntwo\\nthree' | xargs echo 'Items:'") -bash = run.(bash, "echo '1 2 3 4 5' | xargs -n 2 echo") - -IO.puts("\n\n[12] FIZZBUZZ - Combining features") -IO.puts(String.duplicate("-", 60)) - -{result, _bash} = - JustBash.exec(bash, """ - for i in {1..15}; do - if [ $((i % 15)) -eq 0 ]; then - echo "FizzBuzz" - elif [ $((i % 3)) -eq 0 ]; then - echo "Fizz" - elif [ $((i % 5)) -eq 0 ]; then - echo "Buzz" - else - echo $i - fi - done - """) - -IO.puts("\n $ for i in {1..15}; do if [ \\$((i % 15)) -eq 0 ]; then ...") -String.split(result.stdout, "\n") |> Enum.reject(&(&1 == "")) |> Enum.each(&IO.puts(" #{&1}")) - -IO.puts("\n\n" <> String.duplicate("=", 60)) -IO.puts(" All examples executed in a sandboxed virtual filesystem!") -IO.puts(" No real files were touched. Perfect for AI agents.") -IO.puts(String.duplicate("=", 60) <> "\n") diff --git a/mix.exs b/mix.exs index 701d955..376f2c9 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule JustBash.MixProject do use Mix.Project @version "0.1.0" - @source_url "https://github.com/ivarvong/just_bash" + @source_url "https://github.com/elixir-ai-tools/just_bash" @description "A simulated bash environment with virtual filesystem for safe command execution" def project do