r_bash – Telegram
where to correctly set PATH

I ran into a problem where a GUI program can't run a command because it can't find it. I realized it's because I only modify PATH inside .bashrc, so of course a program not started by bash would only see the default PATH.

where should I update PATH?

P.S. the default .profile already sources .bashrc but is somehow not exporting the variables? (otherwise I wouldn't have this problem)

https://redd.it/1ovfib3
@r_bash
i am working on a posix virtual machine with reader macros so i can rewrite the rules of the shell live while its running, to interpret or wite a compiler for any language. moving beyond the syntax of posix by force. i have been told to ask questions rather than assert. i decline.

awkmacrovm(){ awk 'BEGIN{r0=0;r1=1;rx=1;ic=1;while(rx){x=substr(ci,ic,1);y=substr(ci,ic+1,1);z=substr(ci,ic+2,1);if(z == "-"){rx-=ry}; else if(z == "<"){rx=rx<ry}; else if(z == "m"){ry=rx}; else if(z == "$"){rx=system(ry)}; else if(z == "r"){rx=rr[y]}; else if(z == "w"){rr[y] = rx}; else if(z == "i"){getline rx}; else if(z == "o"){printf "%s", rx}; else if(z == "+"){rx=rx ry}; else if(z == "c"){rx = sprintf("%c", ry)}; else if(z == "#"){rx = ord(ry)}; else if(z == "s"){rx=substr(rx,ry,1)};else if(z == "x"){ic=-2;ci=rx}; ic+=3};};' ci="$1";};

https://redd.it/1ovj54b
@r_bash
potd (Pokémon of the Day) - a new random Pokémon for your terminal every day!

[silly little pokemon in the silly little terminal being silly little guys](https://preview.redd.it/ni9gt36fow0g1.png?width=1081&format=png&auto=webp&s=3a4e400a75f29f178f260b58dcc510844e277e4e)

Hi all :)

Just something silly I've been working on since last night. I'm sure it's been done a million times in a million better ways, so if you are aware of any others I'd love to see them!

The noscript uses [pokeget-rs](https://github.com/talwat/pokeget-rs) to retrieve the sprites, so make sure it's available in your $PATH before running this.

Every time the noscript is launched it seeds the $RANDOM value in bash with the current date as a unix timestamp (snapped to 00:00). This value is used to generate the Pokédex ID, ensuring the same Pokémon appears consistently for the entire day.

After that, $RANDOM is seeded again using the current date as a unix timestamp (this time snapped to the last 5 minutes). This value is used to check if the Pokémon should be shiny (1/128 chance), meaning that a Pokémon can be shiny for 5 minutes before the next roll happens.

I also added `--fetch` as an argument which will display a few lines of info next to the sprite:

1. `kn` is the current kernel version
2. `sh` is the current shell
3. `up` is the current uptime
4. `potd` lists the Pokémon's name followed by it's Pokédex ID in parenthesis
5. `seen` shows when you first saw this Pokémon
6. `shny` shows when you first saw the shiny variant of this Pokémon

If you have any tips on how I could improve this, please let me know! It's just a fun little thing I'm working on but I'd love to make it more efficient.

Full noscript:

#!/usr/bin/env bash

for i in "$@"; do
case $i in
"-v" | "--verbose")
# output the pokemon info to stderr
OPT_VERBOSE=1
;;
"-f" | "--fetch")
# include system info alongside the sprite
OPT_FETCH=1
;;
esac
done

CONFIG_DIR=${XDG_CONFIG_HOME:-$HOME/.config} # should almost always return `$HOME/.config`
POTD_DIR="${CONFIG_DIR}/potd" # the config directory that this noscript will use to store everything
POTD_CACHE="${POTD_DIR}/cache" # used to record which sprites have appeared before
POKEGET_CMD='pokeget' # requires pokeget-rs for the pretty small sprites
POKEGET_ARGS=('') # add any extra args for pokeget-rs here
MAX_DEX_ID=905 # max pokedex id in pokeget-rs. decrease this number to limit to specific generations.

# ansi escape codes for colours + style
HEAD='\e[34m'
DATA='\033[38;2;200;166;247m'
DATA='\e[35m'
SHINY='\e[33m'
BOLD='\e[1m'
RESET='\e[0m'

# seed the bash random number with the current date, snapped to midnight, as a unix timestamp
pokemon_seed=$(date --date "00:00" +%s)
RANDOM=${pokemon_seed}
pokedex_id=$((RANDOM % MAX_DEX_ID))
POKEGET_ARGS=("$pokedex_id" "${POKEGET_ARGS[@]}")
sprite_path="${POTD_CACHE}/${pokedex_id}"

# roll for a shiny
shiny_seed=$(date +%s)
shiny_seed=$((shiny_seed - (shiny_seed % 300)))

RANDOM=${shiny_seed}
shiny_roll=$((RANDOM % 128))
if [ $shiny_roll -eq 0 ]; then
POKEGET_ARGS+=('--shiny')
sprite_path="${sprite_path}-shiny"
fi

# check if this sprite has been saved yet
[ -d ${POTD_DIR} ] || mkdir ${POTD_DIR}
[ -d ${POTD_CACHE} ] || mkdir ${POTD_CACHE}
if ! [ -f ${sprite_path} ]; then
echo "$pokemon_seed" >$sprite_path # first seen value
$POKEGET_CMD ${POKEGET_ARGS[@]} >>$sprite_path 2>&1
fi

pokemon_name="$(sed '2q;d' $sprite_path)"
first_seen="$(head -n 1 $sprite_path)"

# extract the shiny encounter date if available
if [ $shiny_roll -eq 0 ]; then
shiny_seen=$(head -n 1 "${sprite_path}")
else
[ -f "${sprite_path}-shiny" ] && shiny_seen=$(head -n 1 "${sprite_path}-shiny") || shiny_seen=0
fi

# get
system info (shamelessly lifted from cutefetch)
get_uptime() {
local up=$(uptime)
up=${up#*up } # remove everything before "up "
up=${up%%,*} # remove everything after first comma
echo $up | xargs # trim whitespace
}

systeminfo=()

if ! [ -z ${OPT_FETCH+x} ]; then
systeminfo[0]="${BOLD}${HEAD}kn${RESET} ${DATA}$(uname -r | cut -f1 -d '-')"
systeminfo[1]="${BOLD}${HEAD}sh${RESET} ${DATA}$(basename $SHELL)"
systeminfo[2]="${BOLD}${HEAD}up${RESET} ${DATA}$(get_uptime)"

# store the pokemon info along with the system info
[ $shiny_roll -eq 0 ] && systeminfo[4]="${BOLD}${HEAD}potd${RESET} ${BOLD}${SHINY}${pokemon_name,,} (${pokedex_id})" || systeminfo[4]="${BOLD}${HEAD}potd${RESET} ${DATA}${pokemon_name,,} (${pokedex_id})"
[ $first_seen -eq $pokemon_seed ] && systeminfo[5]="${BOLD}${HEAD}seen${RESET} ${DATA}today" || systeminfo[5]="${BOLD}${HEAD}seen${RESET} ${DATA}$((first_seen % 60 % 60 % 24)) days ago"

case $shiny_seen in
0)
systeminfo[6]="${BOLD}${HEAD}shny${RESET} ${DATA}never seen"
;;
$pokemon_seed)
systeminfo[6]="${BOLD}${HEAD}shny${RESET} ${DATA}today"
;;
*)
systeminfo[6]="${BOLD}${HEAD}shny${RESET} ${DATA}$((shiny_seen % 60 % 60 % 24)) days ago"
;;
esac
fi

# print the pokemon info to stderr if -v or --verbose was passed
[ -z ${OPT_VERBOSE+x} ] || >&2 echo -e "dex_id: ${pokedex_id}\npkmn_name: ${pokemon_name}\nshiny_roll: ${shiny_roll}\npkmn_seed: ${pokemon_seed}\nshiny_seed: ${shiny_seed}\nfirst_seen: ${first_seen}"

# iterate over every line in the sprite, appending system info when available
index=0
tail -n +3 ${sprite_path} | while IFS= read -r line; do
[ $index -le ${#systeminfo[@]} ] && echo -e "${line} ${systeminfo[$index]}" || echo "${line}"
index=$((index + 1))
done

https://redd.it/1ovk9rn
@r_bash
OC An image compression bash

This is an image compression bash I made to do the following tasks (jpg, jpeg only):

1. Limit the maximum height/width to 2560 pixels by proportional scaling.
2. Limit the file size to scaled (height * width * 0.15) bytes.

\---

#!/bin/bash

max_dim=2560

for input in *.jpg; do

# Skip if no jpg files found

[ -e "$input" ] || continue

output="${input%.*}_compressed.jpg"

# Get original dimensions

width=$(identify -format "%w" "$input")

height=$(identify -format "%h" "$input")

# Check if resizing is needed

if [ $width -le $max_dim ] && [ $height -le $max_dim ]; then

# No resize needed, just copy input to output

cp "$input" "$output"

target_width=$width

target_height=$height

else

# Determine scale factor to limit max dimension to 2560 pixels

if [ $width -gt $height ]; then

scale=$(echo "scale=4; $max_dim / $width" | bc)

else

scale=$(echo "scale=4; $max_dim / $height" | bc)

fi

# Calculate new dimensions after scaling

target_width=$(printf "%.0f" $(echo "$width * $scale" | bc))

target_height=$(printf "%.0f" $(echo "$height * $scale" | bc))

# Resize image proportionally with ImageMagick convert

convert "$input" -resize "${target_width}x${target_height}" "$output"

fi

# Calculate target file size limit in bytes (width * height * 0.15)

target_size=$(printf "%.0f" $(echo "$target_width * $target_height * 0.15" | bc))

actual_size=$(stat -c%s "$output")

# Run jpegoptim only if target_size is less than actual file size

if [ $target_size -lt $actual_size ]; then

jpegoptim --size=${target_size} --strip-all "$output"

actual_size=$(stat -c%s "$output")

fi

echo "Processed $input -> $output"

echo "Final dimensions: ${target_width}x${target_height}"

echo "Final file size: $actual_size bytes (target was $target_size bytes)"

done

https://redd.it/1ow3302
@r_bash
Decompression & Interpretation Of JPEG

As the noscript suggests could you potentially do a decompression of advanced file systems such as JPEG or PNG, but the limitation of using bash builtins (Use ‘type -t {command}’ to check if a command is built in) only, & preferably running ok.

https://redd.it/1owxgcu
@r_bash
Accidentally created an emotional OS

\#!/usr/bin/env bash

\# feralChurchBasementOS.sh

\# emotional maintenance noscript for unstable human systems



set -euo pipefail



OS_NAME="feralChurchBasementOS"

VERSION="v1.0"



log() {

printf "[%s\] %s\\n" "$(date +'%Y-%m-%d %H:%M:%S')" "$*"

}



uninstall_shame() {

log "Purging inherited shame..."

\# placeholder for actual work

sleep 1

log "Shame removed from user-space (may respawn, monitor with journalctl)."

}



reboot_self_worth() {

log "Rebooting self-worth service..."

\# simulate stop/start

sleep 1

log "self-worth.service is now active (running)."

}



patch_trauma_kernel() {

log "Applying trauma kernel patches..."

\# pretend to apply updates

sleep 1

log "Kernel patched. Reboot recommended for changes to take full effect."

}



close_background_processes() {

log "Scanning for emotional background processes..."

\# pretend to kill runaway processes

sleep 1

log "Terminated: overthinking, catastrophizing, people-pleasing."

}



restrict_root_access() {

log "Hardening permissions on /home/heart..."

\# pretend to change ownership/permissions

sleep 1

log "Root access to feelings restricted. Sudo now requires consent."

}



run_health_check() {

log "Running post-maintenance health check..."

sleep 1

log "System status: degraded but functional (expected)."

}



main() {

log "Initializing ${OS_NAME} ${VERSION}..."



uninstall_shame

reboot_self_worth

patch_trauma_kernel

close_background_processes

restrict_root_access

run_health_check



log "Done. For ongoing maintenance, schedule via cron:"

log " 0 3 * * * ${PWD}/feralChurchBasementOS.sh >/var/log/basementOS.log 2>&1"

log "Manual override available with: sudo apt-get heal"

}



main "$@"

https://redd.it/1oyqzgo
@r_bash
Script to re-assemble HTML email chopped up by fetchmail/procmail

I use "fetchmail" to pull down email via POP3, with "procmail" handling delivery, and "mutt" as my mailreader. Long lines in emails are split and wrapped. Sometimes I get a web page as an email for authentication. Usually the first 74 characters of each long line are as-is, followed by "=" followed by newline followed by the rest of the line. If the line is really long, it'll get chopped into multiple lines. Sometimes, it's 75-character-chunks of the line followed by "=".

I can re-assemble the original webpage-email manually with vim, but it's a long, painfull, error-prone process. I came up with the following noscript to do it for me. I call the noscript "em2html". It requires exactly 2 input parameters...
- the original raw email file name
- the desired output file name, to open with a web browser. The name should have a ".htm" or ".html" extension so that a web browser can open it.

Once you have the output file, open it locally with a web browser. I had originally intended to "echo" directly to the final output file, and edit in place with "ed", but "ed" is not included in my distro, and possibly yours. Therefore I use "mktemp" to create an interim scratch file. I have not yet developed an algorithm to remove email headers, without risking removing too much. Here's the noscript...

~~~
#!/bin/bash
if ${#} -ne 2 ; then
echo 'ERROR The noscript requires exactly 2 parameters, namely'
echo 'the input file name and the output file name. It is recommended'
echo 'that the output file name have a ".htm" or ".html" extension'
echo 'so that it is treated as an HTML file.'
exit
fi
tempfile="$(mktemp)"
while read
do
if "${REPLY: -1}" = "=" ; then
xlength=$(( ${#REPLY} - 1 ))
echo -n "${REPLY:0:${xlength}}" >> "${tempfile}"
else
echo "${REPLY}" >> "${tempfile}"
fi
done<"${1}"
sed "s/=09/\t/g
s/=3D/=/g" "${tempfile}" > "${2}"
rm -rf "${tempfile}"
~~~

https://redd.it/1ozghjm
@r_bash
New Project: “GeoBlocker” — US-only SSH Geo-fencing with nftables (feedback welcome!)



https://redd.it/1p05fuu
@r_bash
imgur album fetcher

I'll just leave this here:

for x in $(curl https://imgur.com/gallery/ultimate-4k-wallpaper-dump-2-cats-8Yxub | awk -F 'window.postDataJSON="' '{print $2}' | awk -F '"</noscript>' '{print $1}' | sed 's/\\//g' | jq '.media.[].url' | sed 's/"//g'); do timeout 5 curl "$x" > "${x##*/}"; done

https://redd.it/1p0nbii
@r_bash
New Project: “GeoBlocker” — US-only SSH Geo-fencing with nftables (feedback welcome!)

Hey everyone,

I’m pretty new to sharing code publicly, so please be gentle 😅 — but I’ve been working on something I think could be useful to others, and I’d love feedback from people far more experienced than me.

# 🔒 What is GeoBlocker?

GeoBlocker is a Bash-based tool for Ubuntu 24.04 servers that want to lock down SSH (port 22) to US IP ranges only, using fast-loading nftables sets and geo-IP lists from IPdeny.

Features:

Fetches US IPv4 + IPv6 ranges (with IPdeny usage-limits respected)
Bulk-loads them efficiently into nftables sets (avoiding slow “one CIDR at a time” loops)
Optional SSH whitelist (IPv4 + IPv6)
Investigation mode that shows:
nftables status
whitelist status
SSH client IP
privileges
missing sets or config issues
Backup + atomic write safety
Nothing applied automatically — you stay in control of `/etc/nftables.conf`

Repo is here:

👉 [
https://github.com/baerrs/GeoBlocker](https://github.com/baerrs/GeoBlocker)

# 🛠️ Why I built it

I run a small personal server and kept seeing tons of SSH brute-force attempts from around the world.
Fail2ban helped, but I wanted a stronger approach: just block every non-US address before they even reach SSH.

I found a lot of half-solutions or outdated guides, so I wrote a noscript that:

is reproducible
uses best practices
keeps nftables clean
and is safe for beginners (backups, dry-run behavior, etc.)

# 🙋‍♂️ What I want feedback on

Since I’m new to publishing open-source noscripts:

Is the structure reasonable?
Any obvious improvements to safety, portability, or code style?
Is the README clear enough?
Any red flags for production usage?
Suggestions for features? (cron auto-update? IPv4/v6 country selection? Better logging?)

I’m totally open to constructive criticism — just keep in mind I’m still learning how to present and share code. ❤️

# Thanks in advance!

If anyone has ideas, corrections, or wants to help evolve the project, I’d really appreciate it.
And if even one person finds it useful, that’s a big win for me already.

Thanks! 🙏

— Scott (R. Scott Baer)

https://redd.it/1p11m9l
@r_bash
i am loosing my mind, help me with bash plz.....

[[ -f "$dotfiles_path/installer.sh" ]] && echo found
read -n 1 -s -r
if [ -f "$dotfiles_path/installer.sh" ]; then

ai: (gpt)
 read -n 1 -s -r breaks the [[ ... ]] test output
read -n 1 reads nothing (because Enter is a newline, not a character key),
leaving the newline in the buffer.
So the NEXT read or the control flow may get triggered early.
This results in:
skipped prompts
next input read incorrectly
if branches executing before expected
depending on where the leftover newline is consumed.


is that a thing really in bash noscripting. bro. i lost hours trying fixing this with gemini cli. finally got this from gpt.

i wrote that line to stop to read "found"
cause my program had tui staff so if i hadn't done that the output would vanish.

bro i am noscripting i have no time for that. after that many days coding with bash... finally this????

idk why but after commenting out the read line it works. how can i avoid unintentional situations like that. this interrupts my flow of work.

https://redd.it/1p1vzik
@r_bash
HELP ME

#!/bin/bash

# Decrypt function
function decrypt {
MzSaas7k=$(echo $hash | sed 's/988sn1/83unasa/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/4d298d/9999/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/3i8dqos82/873h4d/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/4n9Ls/20X/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/912oijs01/i7gg/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/k32jx0aa/n391s/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/nI72n/YzF1/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/82ns71n/2d49/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/JGcms1a/zIm12/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/MS9/4SIs/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/Ymxj00Ims/Uso18/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/sSi8Lm/Mit/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/9su2n/43n92ka/g')
Mzns7293sk=$(echo $MzSaas7k | sed 's/ggf3iunds/dn3i8/g')
MzSaas7k=$(echo $Mzns7293sk | sed 's/uBz/TT0K/g')

flag=$(echo $MzSaas7k | base64 -d | openssl enc -aes-128-cbc -a -d -salt -pass pass:$salt)
}

# Variables
var="9M"
salt=""
hash="VTJGc2RHVmtYMTl2ZnYyNTdUeERVRnBtQWVGNmFWWVUySG1wTXNmRi9rQT0K"

# Base64 Encoding Example:
# $ echo "Some Text" | base64

# <- For-Loop here

# Check if $salt is empty
if [ ! -z "$salt" ]
then
decrypt
echo $flag
else
exit 1
fi



Create a "For" loop that encodes the variable "var" 28 times in "base64". The number of characters in the 28th hash is the value that must be assigned to the "salt" variable.

I have tried every single line of code that i know and still didn't get the right answer

https://redd.it/1p1zx87
@r_bash
Concurrency and Strict Mode

I added a chapter about Bash and Concurrency in my small Bash Strict Mode Guide:

https://github.com/guettli/bash-strict-mode?tab=readme-ov-file#general-bash-hints-concurrency

Feedback is welcome:


## General Bash Hints: Concurrency

If you want to execute two tasks concurrently, you can do it like this:

# Bash Strict Mode: https://github.com/guettli/bash-strict-mode
trap 'echo -e "\n🤷 🚨 🔥 Warning: A command has failed. Exiting the noscript. Line was ($0:$LINENO): $(sed -n "${LINENO}p" "$0" 2>/dev/null || true) 🔥 🚨 🤷 "; exit 3' ERR
set -Eeuo pipefail

{
echo task 1
sleep 1
} & task1_pid=$!

{
echo task 2
sleep 2
} & task2_pid=$!

# Wait each PID on its own line so you get each child's exit status.
wait "$task1_pid"
wait "$task2_pid"

echo end


Why wait each PID separately?

- You must wait to reap background children and avoid zombies.
- wait pid1 pid2 will wait for both PIDs, but its exit status is the exit status of the last PID
waited for. This means an earlier background job can fail yet the combined wait can still return
success if the last job succeeds — not what you want if you need to detect failures reliably.



https://redd.it/1p20iz7
@r_bash
Forgot that I added this. Love past me!
https://redd.it/1p38uu0
@r_bash
how do you manage your .bashrc and ,bashprofile

Hi


I'm looking at puppet and setting up standard alias and other things

I don't really want to take over \~/.bashrc or \~/.bash\
profile



I was thinking maybe the way to do this was to add at the bottom

. (or source) \~/.bashrc-puppet

and

. (or source) \~/.bashrc-local


so that what files or other things can add / remove lines to \~/.bashrc puppet can manage the .bashrc-puppet and local mods can go into .bashrc-local

and the same for the bash_profile






https://redd.it/1p4e3yp
@r_bash
Crypto backup tool

It uses zip and gnupg.

**Features**:

* double encrypt: by zip and by gnupg
* generate hash'es from given password with 1000 iterations, it's prevent easy brute force
* once setup: just use symlinks in backup directory
* ready for cron: just use env variable
* simple for code review and modify

[https://github.com/LazyMiB/Crypto-Backup-Tool](https://github.com/LazyMiB/Crypto-Backup-Tool)

https://redd.it/1p4fjhj
@r_bash