A world-writable file can be rewritten by every account on the machine — including the service account your web server runs as. Code executing as www-data through a compromised plugin doesn't need root: any chmod 777 you left behind during past debug...
A 64M scratch tmpfs, a worker process holding its log open on file descriptor 3, and the usual reflex when space runs low: rm the log. du on the directory dropped from 48M to 0. df stayed at 48M used. The next 32M write failed with No space left on d...
Most "the filter looks right but the script misbehaves" bugs come down to three silent failure modes.
First, a missing -r. jq outputs JSON by default, so a string arrives with its quotes attached — your variable holds "web-01", quotes included, and ...
If grep returns empty on a file you know contains matches, suspect the pattern before the file. Default grep uses Basic Regular Expressions BRE, where |, +, ?, and are literal characters unless backslash-escaped. So grep "error|failed" app.log searc...
A demo script called report, two directories on PATH, and one mv between them, all on a scratch tmpfs:
$ report
report 2.1: 14 jobs ok
exit=0
$ mv /opt/jobs/bin/report /opt/jobs/newbin/report
$ ls -l /opt/jobs/newbin/report
rwxrwxr-x 1 root root 42 ...
The trap with rsync is that its dangerous flag combinations don't fail — they complete a different operation than the one you intended, and exit 0. --delete makes the destination match the source, so a mistyped or freshly moved source directory means...
The standard bash alerting pattern ends the same way everywhere you read it: pipe the finding into mail. So I checked what my own box would actually do with that line.
$ type -a mail mailx sendmail
mail not found
mailx not found
sendmail not found
...
I wrote four lines to capture a bash error for a guide. One empty variable, tested twice:
$ cat queue-check.sh
!/bin/bash
count=""
if $count -eq 0 ; then echo "queue empty"; fi
if $count -eq 0 ; then echo "queue empty "; fi
$ bash queue-check.sh...
Four config files, one hostname to rename. I knew the dot in api.internal is a regex wildcard, so I escaped it. grep -rn 'api.internal' unescaped matched a fourth file containing apiXinternal — a typo somebody deliberately kept. Escaped, api\.interna...
I set three log files to known ages with touch -d — exactly 30 days, 30 days and 12 hours, and 31 days — then ran what I thought was a 30-day retention policy:
find . -name '.log' -mtime +30
It returned one file: the 31-day-old one. The 30.5-day f...
A disk almost never fails loudly. It fills a percent at a time until writes stop, and at 100% nothing reports "disk full" — the database throws write errors about its own files, apps crash-loop, and logging a write itself stops recording at the exact...
A four-line YAML file port: 8080, supportemail, transport: tcp, reportdir: /var/reports and the rename most people type first: sed -i 's/port/listenport/g' app.yml. GNU sed 4.9 exited 0 and printed nothing, because -i never does. Afterwards the file ...
SC2086 sits at info, ShellCheck's lowest default severity, and fires on every unquoted $var, so it is usually the first rule anyone disables. I built the smallest test I could: a directory holding quarterly, report.txt and quarterly report.txt, and a...
A script that writes straight to the path a consumer reads can die halfway and leave a torn file that everything downstream loads as complete. No error appears anywhere, because the only process that failed is gone. I tested the fix the unfriendly wa...
A nine-line script, set -euo pipefail on line 2 and an ERR trap on line 3 whose only job is to print the failing command and its line number. A helper function then runs mkdir /proc/export, a directory that cannot be created. On bash 5.3.9 it printed...
Who owns that port? One ss flag answers it without sudo
For months my dev server started on 3001 because something already held 3000, and I kept stepping around it. When I finally asked, ss -ltn 'sport = :3000' showed the port listening on every in...
Level: Intermediate · Time: 7 min · Outcome: A mental model and three guards for unattended cron jobs
There's a gap between a script that works when you run it and a script that survives running unattended for a year. The first only has to succeed o...
Three Lines of Bash Stand Between Your Cron Job and a Silent Outage
Level: Intermediate · Time: 6 min · Outcome: A hardened, ShellCheck-clean cron wrapper generated for you
Three separate incidents taught me the same lesson over about a year. A /1...
Level: Intermediate · Time: 6 min · Outcome: A deploy that waits for its dependencies instead of racing them
I once spent forty minutes at eleven at night debugging a deploy that wasn't broken. The release script ran the database migration, the migr...
My Backup Hadn't Run in 9 Days and Nothing Told Me
Level: Intermediate · Time: 5 min · Outcome: A cron command that can't hang forever
The backup ran fine every night for fourteen months, and then it didn't run for nine days, and nothing told me. ...