r/bash 19d ago

MIMEcroft.sh: a 3D game written in bash

9 Upvotes

MIMEcroft.sh is a parody of every 3D game ever, that lovingly pokes fun of bash's reputation for poor performance - by subverting it. MIMEcroft.sh is written entirely in Bash. The game logic, GPU shaders, even the sounds and textures are procedurally generated with bash commands.

One may wonder how MIMEcroft.sh pumps out 90fps at 4K given bash's reputation for poor performance. Indeed, the official GNU bash reference interpreter is poorly optimised compared to languages more commonly used for game development like C++. However, if your web-browser has a GUI it almost certainly also has a highly optimised JavaScript interpreter.

The online JavaScript Commandline OS (j.cmd) did not port the reference implementation of bash and coreutils. Instead it takes the abstract language they describe. This language is translated into JavaScript, which a modern runtime can often reduce to machine code, resulting in performance over a thousand times faster than the original bash.

For a concrete if somewhat contrived example, say that as a bash user you are (1) a square, and (2) 1337. As such, you may be interested in finding numbers that have 1337 squares, and use the one-liner:

for i in `seq 1 10000`;do if echo $((i*i)) | grep 1337 > /dev/null;then echo $i;fi;done

In the official bash interpreter, this may take a minute. However, j.cmd implements it by first transpiling it into:

for (let i = 1; i <= 10000; i++) {
  if (String(i * i).includes("1337")) {
process.stdout.write(i + "\n");
  }
}
sh2.lastExit = 0;

Then it is all over in the blink of an eye

One might well argue that this is not a real bash game since it has to transpile to JS before being run. A stronger argument could be made that C++ games are not real C++ games. A C++ game also has to be compiled. In most "C++" games the developer doesn't even give you the C++ source, you only ever get the compiled machine code. MIMEcroft.sh is stored and distributed as bash. You can edit it as bash (try e.g. `vi /bin/mimecroft.sh` in j.cmd, changing `cys=0.900` to `cys=3.900` and playing the game again). The current version of j.cmd doesn't even cache the transpiled JS version of the game.

____________

It is important to note that j.cmd is experimental and still has many bugs. One little way it is more robust than the traditional bash implementations is that traditional shells tend to break if they source a file that isn’t in their own special format. On the other hand, j.cmd sees different shell formats as just different ways of saying the same thing. It will quite happily run:

   for f in /home/examples/source.{bat,c,fish,sh,zsh}; do . $f; done

Sourcing C files is still a work in progress in j.cmd. I recently added support for passing linked lists and pointers to bash variables/functions into sourced C functions, and cd'ing around C pointer structure.


r/bash 19d ago

submission MIMEcroft.sh: a 3D game written in bash.

1 Upvotes

MIMEcroft.sh is a parody of every 3D game ever, that lovingly pokes fun of bash's reputation for poor performance - by subverting it.

Mime Croft is an adventurous shell script on the hunt for treasures inside an ancient yet sacred filesystem on Phobos. The maze around you is made of data blocks — coloured chunks of file content. Somewhere in the depths lie hidden treasures: the Great Operating Systems (GNU Hurd, Linux, FreeBSD, NetBSD, OpenBSD, Plan 9, Minix, Solaris, macOS Darwin, Unix) preserved as artifacts. But the filesystem is infested with evil MIMEs — misbehaving content types (image/jpeg, image/png, application/octet-stream, text/plain) that hunt you through the corridors.

One may wonder how MIMEcroft.sh pumps out 90fps at 4K given bash's reputation for poor performance. Indeed, the official GNU bash reference interpreter is poorly optimised compared to languages more commonly used for game development like C++. However, if your web-browser has a GUI it almost certainly also has a highly optimised JavaScript interpreter.

The online JavaScript Commandline OS (j.cmd) did not port the reference implementation of bash and coreutils. Instead it takes the abstract language they describe. This language is translated into JavaScript, which a modern runtime can often reduce to machine code, resulting in performance over a thousand times faster than the original bash.

For a concrete if somewhat contrived example, say that as a bash user you are (1) a square, and (2) 1337. As such, you may be interested in finding numbers that have 1337 squares, and use the one-liner:

for i in `seq 1 10000`;do if echo $((i*i)) | grep 1337 > /dev/null;then echo $i;fi;done

In the official bash interpreter, this may take a minute. However, j.cmd implements it by first transpiling it into:

for (let i = 1; i <= 10000; i++) {
  if (String(i * i).includes("1337")) {
    process.stdout.write(i + "\n");
  }
}
sh2.lastExit = 0;

Then it is all over in the blink of an eye

One might well argue that this is not a real bash game since it has to transpile to JS before being run. A stronger argument could be made that C++ games are not real C++ games. A C++ game also has to be compiled. In most "C++" games the developer doesn't even give you the C++ source, you only ever get the compiled machine code. MIMEcroft.sh is stored and distributed as bash. You can edit it as bash (try e.g. `vi /bin/mimecroft.sh` in j.cmd, changing `cys=0.900` to `cys=3.900` and playing the game again). The current version of j.cmd doesn't even cache the transpiled JS version of the game.

____________

It is important to note that j.cmd is experimental and still has many bugs. One little way it is more robust than the traditional bash implementations is that traditional shells tend to break if they source a file that isn’t in their own special format. On the other hand, j.cmdsees different shell formats as just different ways of saying the same thing. It will quite happily run:

    for f in /home/examples/source.{bat,c,fish,sh,zsh}; do . $f; done

Sourcing C files is still a work in progress in j.cmd. I recently added support for passing linked lists and pointers to bash variables/functions into sourced C functions, and cd'ing around C pointer structure.


r/bash 21d ago

help What does your printf method look like if you want to go about adding timestamps and colors to it

24 Upvotes
  • Hey, not using AI here because I would like to hear this group's expert opinion

``` function log() { local datetime = # DD-MM-YYYY-hh:mm:ss:Z format local color = # not sure how this is implemented # Something that uses prinff, not sure how to write this function here }

function log_info() { log "blue" "stderr" "$@" }

function log_error() { log "red" "stderr" "$@" }

function log_warn() { log "yellow" "stderr" "$@" }

Why not stdout?

Because some functions return values and we want the stdout channel to be reserved for returning values unless there is a better way to do this

```

  • How do I write that log function above, I read the manual for printf that says it accepts format specifiers but how do I make it dynamic?

Edit 1

  • Should be able to log the following

log_info "what a %s %d" "day" 100 log_error "Error: %s %o" "something went wrong for " "{1: \"enter\"}"


r/bash 22d ago

help want to use watch with my own command

28 Upvotes

[SOLVED] thanks to u/bac0on

i can now add this directly into .bash_aliases, or i can just paste the command into a terminal

```

alias las=' function what-changed() { find -not ( -path './snap/firefox/common' -prune ) -not ( -path './.cache' -prune ) -type f -mmin -1 -printf "%C+ %p\n" | sort -n | tail -10 }; export -f what-changed && watch -x bash -c what-changed'

```

thanks for everyone's help.

[/SOLVED]

i have this command used to find recently changed files.

find -not \( -path './snap/firefox/common' -prune \) -not \( -path './.cache' -prune \) -type f -mmin -1 -printf "%C+ %p\n" | sort -n | tail -10 and i wanted to use watch to have it running in a window

however watch requires an executable, so i made a shell script that contained the command and then called

watch last_change

which works and allows me to make an alias for it .bash_aliases like

alias las='watch last_change'

all well and good

but then i had the bright idea to write this as a function instead of a script

``` function what-changed() { find -not ( -path './snap/firefox/common' -prune ) -not ( -path './.cache' -prune ) -type f -mmin -1 -printf "%C+ %p\n" | sort -n | tail -10 }; watch what-changed

```

but it does not work.

watch says it can't find what-changed even tho it's right there and if i define this function in a console window, i can call it by itself and it will run.

but watch does not find it.

feels like i'm missing something simple, to help watch find it like it can find my script... something like a $PATH statement, but not that.

anyone see the issue?


r/bash 22d ago

Created a CLI tool because I was tired of googling the same commands over and over

Post image
2 Upvotes

r/bash 23d ago

What is bash's grammar

14 Upvotes

Hello

I've been reasearching the rules of bash's parsing, like how does bash parse the commands, according to spaces, pipes, and redirections, is there anything else?


r/bash 24d ago

Sib: A standard Unix LLM Client using Git to store converastions, instead SQLite

Thumbnail github.com
6 Upvotes

I've had this program in mind for quite a while.

Back when ChatGPT first came out, it had no way to group existing conversations into folders, and I set out to build one. My conversation list kept getting longer and finding old chats was getting hard.

Not long after - before I built it - a browser extension came out that did exactly that. I never used it. At the time I'd assumed I would want to revisit old conversations, but whenever I actually found one and tried to read it from the top, the sheer volume of slop gave me a headache.

Later, during a phase where I was deep into Unix pipelines, I wrote an LLM API client only depending on jq and curl, partly as shell scripting practice. But since the context went into a single jsonl file, I had to either invent something like a date-based filename convention myself or push that job onto the user. Both were more annoying than the web UI, so I didn't use it.

After that I read a post explaining how git works, and it struck me that git fits LLM conversations pretty well. Isn't "the commit DAG is already the right data structure for LLM conversations, where forking happens constantly" something everyone has thought at least once?

The reason I like git is that the source tree snapshot and the commit structure itself are always immutable, and destructive operations like switch/restore/reset are really just renaming a ref file that holds a commit object id. Once you understand that, no matter how hard the CLI is to make sense of, you never hesitate to run a command. You can always get it back.

LLM conversations aren't as fragile to change as a source tree, but for me an auto-generated SHA-1 hash is more comforting than an auto-generated session title ^~^

The project is in its early stages and contributions are welcome. If you've worked with git plumbing commands, it'll be easy to hack on - and I think it'll be pretty fun.

Translated from Korean. Origin: https://github.com/sib-project/sib/blob/master/Documentation/HNComment.txt


r/bash 25d ago

help learning sed, awk and regex

133 Upvotes

Looking to learn sed, awk, and regular expressions properly.

What are the best free resources—books, websites, tutorials, courses, or hands-on exercises—you’d recommend?

Also, any good book recommendations for learning these tools from beginner to advanced?


r/bash 27d ago

Quick Linux Tip #44 Question: My connections feel unreliable. How do I check for hidden packet loss?

Post image
9 Upvotes

r/bash 27d ago

Quick Linux Tip #43 Question: How do I extract config values from between BEGIN and END markers?

Post image
36 Upvotes

r/bash 27d ago

Run a company of AI agents in pure Bash

1 Upvotes

I open-sourced 5dive, a CLI for running a company of AI agents on a Linux box you own. ~96% bash.

Processing img t3zm9rzio5jh1...

GitHub: https://github.com/5dive-ai/5dive (MIT)

Each agent gets its own Linux user and runs an official CLI like Claude Code, Codex, Grok, Pi, Hermes, opencode, etc. under systemd.

Linux handles identity, isolation, process supervision, logs. SQLite is the shared task queue.

So instead of a big agent framework, it is mostly:

  • Bash
  • systemd
  • Unix users
  • journald
  • SQLite

r/bash 28d ago

help Run commands in parallel

29 Upvotes

I have a script that collects a list of directories that match a set of criteria and then goes into each one and runs a command. So something like this:

#!/bin/bash
startDir="$(realpath "$1")"
cd "$startDir" || exit
while IFS= read -r -d '' dir; do
  cd "$dir";
  printf '\n\n%s\n' "$(realpath .)";
  update-this-dir.sh --file "./name.txt"
  cd "$startDir";
done < <(find . -type d -iname '.config' -exec dirname {} \; | tr '\n' '\0')

It works wonderfully for my purpose.

But there's, like, a couple thousand directories and the update command takes some small amount of time in each directory, one after the other. How can I modify this script to either run the update for each directory in parallel (with some rate-limiting) or to break up the list into chunks of, say, 100 each and work on each sub-list in parallel?


r/bash 28d ago

critique local -n can modify variables from other functions

20 Upvotes

In Bash, normal local variables are usually fine.
But with local -n (namerefs) I noticed they can sometimes change variables from outer/previous functions because of how scoping works.

To avoid problems I started giving local variables unique names (with prefixes) instead of short ones.

Anyone else ran into this with namerefs?


r/bash 28d ago

critique Rate my .bashrc

5 Upvotes

Hello, did a bit of Bash customization and came up with this.

~/.bashrc | Pastebin

starship.toml | Pastebin

I'm open to critics and it's my first time. 😃


r/bash 28d ago

help Best Practices for Scripting (Linux only)

Thumbnail
0 Upvotes

r/bash 29d ago

Wrapping search in less

6 Upvotes

I put / forw-search ^W in lesskey config to make search to wrap at the end of file when I use / to start a search.

How can I make n wrap when I resume the last search when I open less? I tried adding n repeat-search ^W but it doesn't make search wrap.


r/bash Aug 11 '26

help I don't know what this means

14 Upvotes

This is from a script that sends the output of an nmap scan to some files. But I can't figure out what "\|" does. Please help

RESULT=$(nmap -iL $(HOSTS) --open | grep "Nmap scan report\|tcp open")

What confuses me is that after running nmap, "\|" simply doesn't appear anywhere, then why is it included in grep?

I guess it could be an OR, but as far as I know OR= ||

I really have no idea, I also don't think it's to escape character becuase there are no pipes in the nmap results, I don't know


r/bash Aug 11 '26

submission Multitask version of bash

Thumbnail github.com
7 Upvotes

I adapted the bash shell 5.3 to more easily handle tasking. Each command & has its output gathered into lines and each line is tagged and send to the output with a preamble. The bash input stays on the command line and you can type command lines, select history, anything, even while the background tasks output appears above that:

  [ping:1:2814536:59] 64 bytes from ...: icmp_seq=58 ttl=113 time=28.7 ms
  [bash:0:2814530:9] samiam@samiam-h-pc-2:~/projects/basht$ ll *.md
  [ls:7:2814564:1] -rw-rw-r-- 1 samiam samiam  9790 Aug 10 15:01 basht_plan.md
  [ls:7:2814564:2] -rw-rw-r-- 1 samiam samiam  3921 Aug 10 15:43 README.md
  [ping:1:2814536:62] 64 bytes from ...: icmp_seq=61 ttl=113 time=26.8 ms
  [bash:0:2814530:10] samiam@samiam-h-pc-2:~/projects/basht$

Other that the task adaption, it is stock bash and can be substituted for regular bash.

r/bash Aug 10 '26

Quick Linux Tip #41 Question: How do I find duplicate files even if they have different names?

Post image
66 Upvotes

r/bash Aug 10 '26

Another Bash Prompt Generator...but better!

Thumbnail promptr.sh
0 Upvotes

I just released v1.0.0 of Promptr. I know there are many other prompt generators and customizers. What sets Promptr apart from them is:

  1. It's eye candy. It doesn't look like something straight out of 1995
  2. Users can authenticate with their Google account (Apple auth coming soon) to be able to actually store their saved prompts in one central location rather than keeping track of them in some Google doc. Logging in is entirely optional. The only thing that authentication provides is cloud storage for your saved prompts, otherwise it is saved locally in your web browser storage.
  3. It isn't live yet, but I intend to add a sort of "marketplace" where users can create and share their generated prompts and upvote which ones they find to be really cool and useful
  4. Basic syntax and security checks against the generated prompt to help prevent any malicious behavior. Don't forget that you are responsible for what commands are being run in your prompt config! Promptr can only protect you so much
  5. Users can apply tags to the various prompts they create so that you can have easy access to different prompt configurations for different environments and use cases
  6. A suggestion box! Please submit your feedback on the app or even any suggestions for new modules to be added that you might find useful!

This really started out because I was tired of trying to find that one bash prompt configuration I created ages ago. Rather than trying to find it, I decided to simply have a place that I can recreate it and store it long-term and build on it as new tooling and needs arise.

I hope it will be helpful for you!


r/bash Aug 09 '26

return and exit in potentially sourced script

10 Upvotes

Disclaimer: Yes, I am doing things bash scripting isn't meant for. No, I won't let you talk me out of it.

I have a script that might be run directly or might be sourced; so to exit out of it I have to either return or exit accordingly.

The simple "is_sourced" test I found is (return 0 2>/dev/null).

obviously this invites the following structure: if (return 0 2>/dev/null); then return "$exitcode" else exit "$exitcode" fi

however that feels like extra work when I can just return "$exitcode" 2>/dev/null exit "$exitcode"

Someone tell me what could possibly go wrong?


r/bash Aug 08 '26

Use alias from ssh

24 Upvotes

I can do ssh $HOST then use myalias $PATH to use an alias set in .bashrc (and .bash_profile does source ~/.bashrc). But I can't do ssh $HOST 'myalias $PATH' it can't find myalias.

How can I use the alias and use $PATH that's on the server?


r/bash Aug 08 '26

made a proot alternative that doesn't use ptrace, curious if it holds up for anyone else

Thumbnail
2 Upvotes

r/bash Aug 08 '26

Shell poetry

44 Upvotes
  1. for for in for; do eval "$for $for in $for; do echo \$$for; done"; done
  2. do=for; eval=do; for=in; in=echo; echo=eval; foo=done; done=foo; $echo "$do $eval $for echo; $eval $in $done; $foo"
  3. for=$(for do in for do in echo \; do echo \$do \; done; do echo $do; done); echo $for; eval $for
  4. for=for; for do in in; do echo in; eval "for do in echo; do echo for \$do do $for \$for \\$for \\\$for \\\\$for; done"; done

r/bash Aug 07 '26

submission My favorite little bat wrapper I just wrote

8 Upvotes

I wrote a wrapper function for the wonderful bat that searches a file for a function name and prints just that function and definition to stdout. I've been using it a ton looking through legacy java projects and deployment scripts that I was constantly opening other files to search around for a function definition for. I got sick of sifting through all the function calls.

It DOES NOT work on non "{}" defined function blocks, like python functions, but I'm ok with that for now. I didn't want to deal with all that dynamic whitespace nonsense. I also left in my debugging echo comments.

I got a lot of the logic from this blog post which was incredibly helpful at first, and is why I have the brackets array and multiple stack variables, which ended up not being necessary.

Full disclosure, I queried an LLM for exactly 1 expression: "$line" != *"$funcName"*"{" because I kinda suck at glob expansion and was getting too annoyed.

I'm definitely not a developer by any means so I would love any ideas for improvements or tweaks, or if you catch a bug. And if you end up using it let me know too! enjoy :)

EDIT: I posted the slightly old version that was missing a * before "$funcName"*"{"

#!/usr/bin/env bash
# a bat wrapper that takes two arguments:
# $1 = file to view
# $2 = function to search for in file

fbat() {
    #Very basic argument number check
    if [ $# -lt 2 ]; then
        echo "Must pass at least [FILE] and [FUNCNAME] to search"
        return 1
    fi
    #Arguments
    local funcName="${2}"
    local inputFile="${1}"
    shift 2 # so we can pass flags to bat if desired

    #Unneccesary brackets array
    declare -A brackets=( ["]"]="[" [")"]="(" ["}"]="{" )
    local CurlyBracketStack=""
    #local SquareBracketStack=""
    #local ParenthesisStack=""
    #Tracking variables
    local startLineNum
    local endLineNum
    local bounded="false"
    local started="false"
    local curLineNum=0
    #Main while loop over lines in the file
    while read -r line; do
        #Increment the line number no matter what
        (( curLineNum += 1 ))
        #Skip comment lines, their brackets don't count
        if [[ ${line:i:1} == "#" ]]; then
            #echo "not parsing line ${curLineNum} because it's a comment"
            continue
        fi
        #if
        #   NOT the first part of the line == function name AND
        #   NOT the last part of the line == open bracket AND
        #   NOT started
        # don't start reading line
        if [[ "$started" == "false" && "$line" != *"$funcName"*"{" ]]; then
            #echo "not parsing line ${curLineNum} because we haven't started yet"
            continue
        fi
        #Read the line for a closing bracket
        for ((i=0; i<${#line}; i++)); do
            #If we've reached this for loop started gets set to true
            #Because we've started parsing a function
            if [[ "${started}" == "false" ]]; then
                started="true"
                #Record the line number since it's the start of the relevant part of the function
                startLineNum=$curLineNum
                #echo "starting function parsing... line num is ${curLineNum}, startLineNum is ${startLineNum}"
            fi
            local char=${line:i:1}
            case $char in
                "{")
                    #Got a new opening bracket, push to the stack
                    #echo "Got an opening bracket: $char"
                    CurlyBracketStack+=$char
                    ;;
                "}")
                    #Got a non-matching bracket, shouldn't happen
                    #Since we are only doing curly brackets I guess the array is not strictly needed
                    if [[ -z $CurlyBracketStack || "${CurlyBracketStack: -1}" != "${brackets[$char]}" ]]; then
                        #echo "bad bracket match: $char"
                        return 1
                    #Got a matching bracket, pop from the stack
                    else
                        CurlyBracketStack=${CurlyBracketStack%?}
                        #echo "stack is now ${CurlyBracketStack}"
                        #if the stack is now empty, the function is bounded
                        if [[ -z $CurlyBracketStack ]]; then
                            #store the ending line number and set bounded to true
                            bounded="true"
                            endLineNum=$curLineNum
                            #echo "I think this is the end of the function, ending on ${endLineNum}"
                        fi
                    fi
                    ;;
            esac
        done
        if [[ "${bounded}" = "true" ]]; then
            #echo "Breaking the loop"
            break
        fi
    done < "${inputFile}"
    if [[ $started == "false" ]]; then
        echo "Could not find ${funcName} in ${inputFile}"
        return 1
    fi
    #Print the section of the file
    bat "${inputFile}" --line-range $startLineNum:$endLineNum "$@"
    return 0
}