Auditing every user’s authorized_keys in a Bash loop with jc + jq
I maintain [jc](https://github.com/kellyjonbrazil/jc), which converts command output and common file types to JSON so you can query them with `jq` instead of `grep`/`awk`/`cut`. A couple of newer parsers turned out to be handy in Bash scripts, so I wrote up some examples.
**Why not just awk?**
`authorized_keys` looks like a whitespace-delimited file, but it isn't. Lines can start with an optional, comma-separated options field, and option values can be quoted strings with spaces in them. So the field positions shift from line to line. Here's a sample file:
ssh-ed25519 AAAAC3Nza... kelly@laptop
command="/usr/bin/rsync --server --sender -logDtpre.iLsfxC . /srv/backup",no-port-forwarding,no-pty ssh-rsa AAAAB3Nza... backup@nas
from="10.0.8.0/24",no-agent-forwarding ecdsa-sha2-nistp256 AAAAE2Vj... ci-runner@build01
ssh-ed25519 AAAAC3Nza...
The obvious `awk` one-liner to print the key type and comment:
$ awk '{print $1, $3}' ~/.ssh/authorized_keys
ssh-ed25519 kelly@laptop
command="/usr/bin/rsync --sender
from="10.0.8.0/24",no-agent-forwarding AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz...
ssh-ed25519
Only the first line is right. Looking for RSA keys with `awk '$1=="ssh-rsa"'` returns nothing, because the one RSA key has options in front of it.
`grep` has the opposite problem: it matches text anywhere on the line. `grep -v 'command='` to find unrestricted keys also drops a key whose *comment* says "no command= needed". And `grep ssh-rsa` matches a restricted ed25519 key whose forced command happens to contain the string `ssh-rsa`.
With `jc --authorized-keys`, every line comes out with the same fields, so there's no guessing:
$ jc --authorized-keys < ~/.ssh/authorized_keys | jq -r '.[] | "\(.type) \(.comment // "(no comment)")"'
ssh-ed25519 kelly@laptop
ssh-rsa backup@nas
ecdsa-sha2-nistp256 ci-runner@build01
ssh-ed25519 (no comment)
`options` is always an array, quoted values like `command="echo ssh-rsa disabled; exit"` stay intact, and multi-word comments are kept whole.
**Tag every key with its owner, then query the whole box**
A loop adds a `user` field to each key and emits JSON Lines:
for f in /home/*/.ssh/authorized_keys; do
u=$(echo "$f" | cut -d/ -f3)
jc --authorized-keys < "$f" | jq -c --arg u "$u" '.[] | . + {user: $u}'
done > keys.jsonl
Now each question is just a different `jq` filter. Which keys have no `from=` or `command=` restriction?
jq -r 'select(any(.options[]; startswith("from=") or startswith("command=")) | not)
| "\(.user) \(.type) \(.comment // "(no comment)")"' keys.jsonl
Because this checks the `options` array instead of the whole line, comments and key data can't cause false matches.
Is the same key authorized for more than one user?
jq -s -r 'group_by(.key)[] | select(length > 1)
| "\(.[0].comment // "(no comment)"): \(map(.user) | join(", "))"' keys.jsonl
Which legacy RSA or DSA keys are still around?
jq -r 'select(.type == "ssh-rsa" or .type == "ssh-dss") | "\(.user) \(.comment)"' keys.jsonl
(Run the loop with `sudo` if you need to read other users' files.)
**Bonus: a change-freeze guard from a shared calendar**
`.ics` files are worse for line tools: long lines get folded onto the next line, and parameters are jammed into property names (`DTSTART;TZID=America/Los_Angeles:20261019T090000`). The `--ics` parser unfolds all of that and normalizes timestamps to ISO 8601. That means `jq`'s `fromdateiso8601` and `now` can check whether you're inside a freeze window, and `jq -e` turns the result into an exit code:
if jc --ics < freeze.ics | jq -e '
[.[] | select(.categories[]? == "Freeze")
| select((.dtstart | fromdateiso8601) <= now and now < (.dtend | fromdateiso8601))]
| length > 0' > /dev/null
then
echo "Change freeze in effect. Aborting deploy." >&2
exit 1
fi
(This works with UTC timestamps, the ones ending in `Z`. Events with a `TZID` come
A coding agent in ~60 lines of bash, curl and jq (and the shell tricks it took to get there)
I wanted to understand what tools like Claude Code actually do under the hood, so I built the smallest version I could in plain bash. Three scripts, each adding one idea: a single curl call, then a chat loop, then a "bash" tool the model can call. The last one is a working agent that, when asked, writes and runs a Tetris game in Python.
Repo: https://github.com/antonum/bash-harness
The AI part turned out to be boring: it's a chat loop with an inner loop that runs commands until the model stops asking for them. The interesting part was the bash. A few things I hit that might be useful to others:
**Never put big payloads on the command line.** The conversation gets resent in full on every request and only grows. One `cat` of a large file as an argument blows past ARG_MAX (1 MB on macOS). So the request body is built by jq and piped into curl with `-d @-`, and tool output goes into jq via `--rawfile` from a process substitution:
```bash
add_message --arg id "$call_id" --rawfile c <(printf '%s' "$result") \\
'{role: "tool", tool_call_id: $id, content: $c}'
```
**One generic `add_message` function.** It takes jq args plus a jq object expression, so each call site spells out the exact JSON shape it is appending. No templating, no escaping bugs:
```bash
add_message() {
MESSAGES=$(jq --argjson m "$(jq -nc "$@")" '. + [$m\]' <<<"$MESSAGES")
}
add_message --arg c "$input" '{role: "user", content: $c}'
```
**Capturing output and exit code from an arbitrary command** without tripping `set -e`:
```
run_tool() {
local output status=0
output=$(bash -c "$1" 2>&1) || status=$?
printf '%s\\n[exit code: %d\]' "$output" "$status"
}
```
**Iterating a JSON array one element per line** with `jq -c '.tool_calls[\]'` fed through `done <<(...)`, so the loop body can touch variables in the parent shell.
**`read -e -r -p`** gives you readline editing and history for free in the prompt loop.
**A one-line "thinking" spinner** in a background subshell, killed with `kill $!` when curl returns.
Caveats: it needs bash 3.2+ (runs on stock macOS), jq, curl, and an API key for any OpenAI-compatible endpoint with tool calling, including local models. And obviously script 03 runs whatever the model asks for, with your permissions and no confirmation. It's a tutorial, not a product. Read the commands as they scroll by and don't run it somewhere you care about.
Happy to hear what I got wrong or what you'd do differently. The repo has a "what's next" list (approval prompts, output limits, session resume) if anyone wants to extend it.
https://redd.it/1xarji9
@r_bash
I built a Bash practice site that runs in your browser. Looking for feedback and contributors
Hi everyone! I’ve spent the last few days building learnbash.dev, a site for practising Bash and Linux commands without installing anything or touching your own files.
It runs a real Bash shell on Alpine Linux with GNU tools, entirely in your browser through v86. Each challenge gives you a task, checks the result, and offers hints if you get stuck, followed by an explanation.
There are currently 30 challenges across two parts:
Navigation and files: moving around, reading and editing files, copying, moving, deleting, and working with wildcards.
Investigating an incident: using find, grep, pipes, redirection, sorting, and permissions to work through a small incident scenario.
It’s free, with no accounts or ads, and progress is saved locally in your browser.
I’d appreciate feedback from beginners: are the instructions clear, do the explanations help, and are there places where the difficulty jumps too quickly? If you’re experienced with Bash, corrections to the teaching material and suggestions for better exercises would be especially useful.
For the next section, I’ve drafted exercises on cleaning up text exports with tools like diff, cut, tr, and sed. I want to get feedback on the existing sections before adding more. Longer term, I’d like to cover variables, quoting, scripting, processes and job control, and more involved practical projects.
I’m also exploring networking exercises. For example, running local web and SSH servers inside the VM so learners could practise curl, ssh, scp, and SSH keys without external machines. That still needs prototyping to understand the limitations, performance, and effect on download size.
Contributors are welcome, whether you’re interested in writing challenges, reviewing explanations, fixing bugs, improving tests, or exploring the networking ideas. Challenges use Markdown and small Bash scripts for setup, checking, and reference solutions, so you don’t need frontend experience to help with the exercises. The repository includes authoring docs and a curriculum.
Source: github.com/adneborresen/learnbash.dev
Feel free to open an issue or pull request, or comment here if you’d like to get involved, have feedback or just want to discuss. You can also submit feedback directly through the site.
https://redd.it/1x9wiig
@r_bash
I built a network security suite on my phone using Termux, Java, and Bash. I automated the setup with a single shell script. Ready to get roasted.
https://redd.it/1x92baj
@r_bash
Is this possible? Want to use terminal to show Youtube homepage with slightly better ux.
New to Linux. Installed Ubuntu. Keep reloading youtube home page because recommendations suck. Want to to avoid shorts and other unneeded features by using Youtube solely through bash. Want to know if small quality of life changes can be made.
I'm wondering if it's possible to use the terminal to show a small number of videos from my home page, title with no thumbnail is enough, which shows dislike and like count, make it so that pressing not interested or don't recommend channel is a bit faster to click.
Sorry if this is a weird or absurd ask. I'm going cold turkey on AI overview, but want to see if there should be anything I need to know about starting a project like this.
https://redd.it/1x8aw96
@r_bash
Unset doesn't properly work in functions??
So this is super weird. When I run the following in a script (or manually in the terminal), it does exactly what you'd expect.
declare -A myassoc
myassoc[key]="Hello World"
unset myassoc
declare -p myassoc
echo "This should be empty: ${myassoc[@]}"
./dev2: line 9: declare: myassoc: not foundThis should be empty:myassoc is unset and therefore undeclaredfoo () {
declare -A myassoc
myassoc[key]="Hello World"
unset myassoc
declare -p myassoc
echo "This should be empty: ${myassoc[@]}"
}
foo
declare -- myassocThis should be empty:declare -p to test if variables exist regardless of their type and content.test is not a viable alternative, since it doesn't have the same behaviour for local myvar and declare myvar (vars without value return false, even if declared)bar () {
declare -A myassoc
myassoc[key]="Hello World"
declare -gp myassoc
echo "This should be empty: ${myassoc[@]}"
foo () {
unset myassoc
echo "In foo:"
declare -gp myassoc
echo "This should be empty: ${myassoc[@]}"
}
foo
echo "After foo:"
declare -gp myassoc
echo "This should be empty: ${myassoc[@]}"
}
bar
is there a way to improve a github release updater to pick the right asset name?
i made a script that actually works for most times to update brave browser, but for packages that change names its trivial. the idea is to place, update directory with a file with metadata that i can extract. here is the script :
updatePrograms() {
for dir in ~/Programs/*; do
#meta file structure
if [[ -f "$dir"/update/meta ]]; then
local metadata=$(cat "$dir"/update/meta)
local owner=$( echo "$metadata" | grep "Owner" | sed 's|Owner:||' )
local project=$(echo "$metadata" | grep "Project" | sed 's|Project:||')
local output=$(curl -fsS "https://api.github.com/repos/$owner/$project/releases/latest")
[[ -z "$output" ]] && { echo "no response for $project"; continue; }
local checkVersion=$(jq -r '.tag_name' <<< "$output")
echo "updatable Project : $project"
[[ "$checkVersion" = "null" ]] || [[ -z "$checkVersion" ]] && { echo "couldn't fetch version for $project"; continue; }
local currentVersion=$(echo "$metadata" | grep "Version" | sed 's|Version:||')
[[ "$checkVersion" = "$currentVersion" ]] && { echo "$project is up to date : $checkVersion"; continue; }
while true; do
read -p "$project is on $currentVersion, latest is $checkVersion; do you want to request the latest Version? [y/n]: " response
case "$response" in
[Yy])
echo "Updating.."
local name
name=$(echo "$metadata" | grep "AssetName" | sed 's|AssetName:||')
local url=$(jq -r --arg name "$name" '.assets[] | select(.name == $name) | .browser_download_url' <<< "$output")
if [[ -z "$url" ]] || [[ "$url" = "null" ]]; then
echo "couldn't find asset matching $name"
echo "you may need to change asset name version to $checkVersion"
break
fi
if curl -fL -o "$dir/update/$name" "$url" ; then
sed -i "s|Version:.*|Version:$checkVersion|" "$dir"/update/meta
else
echo "Downloading falied"
break
fi
break;;
[Nn])
echo "not Updating"
break;;
*)
echo "choose y or n";;
esac
done
fi
done
}
https://redd.it/1x66ycu
@r_bash
wiki how ?!
i installed termux and went to the wiki to learn it but for some reson all of the wikis [ arch wiki, official wiki, unofficial wiki, even \] stops with a prof u'r not a robot infenit bage and never work and even github won't upload the page not termux repo or any other repo , i know the problem is with me ; so do you know any other place to learn termux/terminal tools no matter if it's a wiki, a site, or even a book.
* i don't have a computer so don't tell me to use a one
https://redd.it/1x3w9zc
@r_bash
MountSync – A utility I built to sync configs, MP3s, and AI skills across my Linux PCs via rclone
Hi everyone,
I wanted to share a tool I built to solve a personal daily headache: MountSync (mosy).
I use multiple Linux machines, and I needed a reliable way to share active files between them: specifically application configurations, a local MP3 music library, and local AI skill/context folders. I wanted to keep files exactly where they belonged on the local filesystem while seamlessly updating them across PCs in the background.
Since it solved a real-world problem for me, I polished it, added testing, and open-sourced it as a portfolio project.
### What problem does it solve?
Managing rclone mount manually, copying files, creating symbolic links, and making sure systemd services start correctly on every new PC is tedious. MountSync automates the whole process:
1. Auto-mounts: Sets up and manages your rclone mount in the background via systemd user services.
2. Auto-linking: When you run mosy add ~/Music, it moves the target directory to your cloud drive (like Google Drive or OneDrive) and replaces the original path with a clean symbolic link.
3. Instantly sync new PCs: On any new PC, you just install it and run mosy init. It automatically maps all symlinks to your existing cloud folders, syncing your configs, music, and dotfiles in seconds.
The codebase is 100% open source on GitHub:
github.com/GabrielTeixeiral0l/MountSync
I’d love to hear your thoughts, feedback on the code structure, or suggestions for new features!
https://redd.it/1va2fat
@r_bash
Would it be good to Learn Bash scripting, or should i go with a "normal" language, like Python, before that?
Getting interested on messing with Linux and CLI stuff, I'm just confused on what exactly to start with. Any recommendations?
https://redd.it/1v9z70a
@r_bash
History not working on a Ubuntu 26.04 VM
Arrowing back through my command history isn't working for some reason. I have HISTORYSIZE set as 0 in .bashrc, as well as these lines at the bottom:
bind '"\\e[A": history-search-backward'
bind '"\\e[B": history-search-forward'
They're even in the systemwide bashrc file.
What is going on?
https://redd.it/1vawz3g
@r_bash
I built a tool that automates setting up WireGuard remote access VPNs along with other network configurations. It also generates QR codes for clients and lets you check the status of all WireGuard tunnels
https://redd.it/1ve6my2
@r_bash
Terminal Based Universal-Downloader - Supports Youtube/Flathub/Github/Torrents/Direct Links
https://github.com/djkido316/Universal-Downloader
https://redd.it/1verwu8
@r_bash
Bash is way too fun
I'm a construction worker and love tech, switched to Linux last year and wanted to learn BASH just out of curiosity not necessity. The only other language I have experience is GML from when I was a teen.
So I've been building a text adventure and BASH is so fun and for strings it's easy! I've kept it pure bash except for using sleep, I think I understand I could have used read instead of sleep but sleep seems pretty ubiquitous on most computers.
It's messy and definitely an elementary project so I've kept trying to implement different design elements like sourcing data and turning that into arrays, using name refs (I'll be honest using name refs confuses the shit out of me), parsing CSVs, state machines, saving (declare -p) , loading, ect.
If any of you new guys wanna practice something you can easily take on without any AI I advise trying to build a text adventure and not just navigation but include inventory and other mechanics.
And if you're an absolute beginner YSAP bash tutorial was king for me, plus man, and tldr
https://redd.it/1vfk2qh
@r_bash
Login with ssh password with bash only
I can use programs like sshpass/expect to give a ssh password to the ssh command if I don't have pubkey auth. Can I do it using just bash?
Some programs let you pipe a password on stdin can I do anything for ssh?
It is to automate first login with password so I can create add the pubkey auth.
https://redd.it/1vhc093
@r_bash
I Made a yt-dlp script
First of all, please excuse my English. I’m using a translator, sorry! :(
I’m a regular user of the tool yt-dlp, but it can sometimes be a bit complicated to use. I’ve created a Bash script built WITHOUT AI (it’s wild that we even have to specify this nowadays...) that makes it easier to use. It’s currently in Spanish, but I’ll be adapting it into English over the next few days. Don’t worry, it’ll only take me a few minutes.
I’m sharing it in case anyone finds it useful! :)
Here’s the Codeberg repository:
https://codeberg.org/elrefugiodeunnerd/simple-yt-dlp-script
https://redd.it/1xaz66b
@r_bash
Need help with small issue that pisses me off
/r/KittyTerminal/comments/1x91gba/need_help_with_small_issue_that_pisses_me_off/
https://redd.it/1xaedn2
@r_bash
Help: Does anyone use AI to assist with making Scripts
Does anyone use AI to assist with making bash scripts? I just want to hear y'all opinion on it.
https://redd.it/1x9b044
@r_bash
rethinking bash2json: introducing shelljq
https://github.com/Tirito6626/shelljq
https://redd.it/1x95tj7
@r_bash
Alias for Safety? Creating Alias for more than one word?
/r/linux4noobs/comments/1x86fbl/alias_for_safety_creating_alias_for_more_than_one/
https://redd.it/1x86fhu
@r_bash
Tinybackup: a tiny Kopia backup wrapper
For all those who want simple backups of their machines, I have created a tiny bash wrapper around Kopia: https://github.com/bderenzo/tinybackup
From a single minimal config file, it handles repository setup, retention, ignore rules and pre/post backup hooks, etc.
It can back up to a local disk, SFTP, S3 or any rclone remote, and lets you browse snapshots through CLI, a mount point or the Kopia web UI.
https://redd.it/1x630n8
@r_bash
Many things could go wrong with this script and leave a running process, how would you recommend fixing this?
#!/usr/bin/env bash
function main() {
# Phase 1: Setup and Run Dev Server
printf "%s\n" "Starting development server..."
cd "$HOME/Desktop/my-project-1" || exit 1
# Run the dev server in the background
rm -rf coverage dist && npm run lint && npm run coverage && npm run test && npx tsc || exit 1
npx tsx watch --env-file=.env.development src/index.ts > "/tmp/server.log" 2>&1 &
DEV_PID=$! # Store the Process ID of the 'dev' server
# Monitor for "server is listening"
printf "%s\n" "Waiting for server startup message..."
while ! grep -q "Server is now listening" "/tmp/server.log"; do
printf "%s\n" "Server not yet ready. Still waiting..."
sleep 1 # Wait 5 seconds before checking the log again
done
printf "%s\n" "SUCCESS: Server is listening. Proceeding to Phase 2."
# Phase 2: API Call and Wait
printf "%s\n" "Executing API call..."
rm -rf /tmp/test_app && curl -X POST http://localhost:3003/job/node/express/basic/generator
# Monitor for "downloading..." message in logs...
printf "%s\n" "Waiting for download completion..."
# Wait for the download confirmation
while ! grep -q "downloading docker container files completed successfully" "/tmp/server.log"; do
printf "%s\n" "Waiting for download confirmation..."
sleep 1
done
# Phase 3: Cleanup
printf "%s\n" "Shutting down services..."
kill ${DEV_PID} # Use the stored PID to gracefully stop the server process
printf "%s\n" "Moving into generated code..."
cd "/tmp/test_app" && npm install && code "/tmp/test_app" && cd "$HOME" || exit 1
}
main "$@"
Add and subtract times
How can I use date to add and subtract times? I want to ignore time zones and just change the time. For example date -d "21:00:00 +02:02:30 -01:10" +%"T" should show 23:01:20.
https://redd.it/1v98u10
@r_bash
A small bash tool for sandboxing AI coding agents on linux
/r/CLI/comments/1x34iix/a_small_bash_tool_for_sandboxing_ai_coding_agents/
https://redd.it/1x34prg
@r_bash
Vi mode history isn't consistent
In vi mode up and down arrows goes through history and puts the cursor at the end of the line but j and k puts it at the beginning. They are all bound to previous-history and next-history commands so why is it different? Can I make j and k work like up and down?
CTRL+p and CTRL+n are also bound to previous-history and next-history but it's not going through history it's going through a list of all commands it can find.
How can I make everything the same as up and down arrows?
https://redd.it/1vafuk7
@r_bash
Almost a year of work—how are these? Using only rofi and completely written in bash.
https://redd.it/1vclxa1
@r_bash
Can AI actually write production-ready scripts end-to-end without human intervention?
/r/linuxquestions/comments/1x2lto3/can_ai_actually_write_productionready_scripts/
https://redd.it/1x2lu1q
@r_bash
Linux Interview Question
Today I had a mock interview with a senior Linux administrator, and he asked me a question that completely caught me off guard:
"Can you collect CPU, Memory, and Disk usage in a Bash script without using top, free, df, or any external commands?"
My immediate answer was No.
Honestly, I've been working with Linux for years, but I've always relied on standard commands and monitoring tools to troubleshoot systems. I never stopped to think about where those commands actually get their data from.
The interviewer then gave me a hint about the /proc and /sys filesystems. That completely changed my perspective. I realized these commands are just reading data that the kernel already exposes.
But then came the follow-up questions, and once again I was stuck:
Is reading directly from `/proc` and `/sys` the correct approach?
How would you calculate CPU utilization using /proc/stat?
How would you determine memory usage from `/proc/meminfo`?
How would you calculate filesystem usage without relying on df?
I found this really interesting because it tests your understanding of Linux internals rather than just your ability to use commands.
Has anyone here been asked similar questions in Linux SysAdmin, DevOps, or SRE interviews? I'd really appreciate any explanations, learning resources, or examples on how you'd answer these follow-up questions. It help me for my preparation for Linux interviews
https://redd.it/1veg8ir
@r_bash
I built a visual game entirely in pure Bash — here is the demo and full source code!
Hey everyone,
I wanted to show what's actually possible using just pure Bash—from custom terminal graphics and animations to game logic.
I've been working on a project called Alchemon, a game written completely in Shell script that pushes terminal visual output to its limits. No external heavy frameworks, just raw CLI magic!
📹 Watch the gameplay demo on YouTube: \> https://www.youtube.com/watch?v=yFOpOPtk-Sc
💻 Check out the source code on GitHub: \> https://github.com/mrugaladamian/alchemon
Feel free to clone it, test it out, or drop any feedback/ideas. Hope you enjoy it!
https://redd.it/1vg6690
@r_bash
mirror-repo: Auto-sync Gitlab and Github repositories
https://redd.it/1vhux6o
@r_bash