Day 42 - Using Bash Loops for Monitoring, SSH Key Exchange & Remote Execution
Automate DevOps Workflows: The Role of Bash Loops in Monitoring, SSH Key Exchange & Remote Execution

Today I dove deep into loops in Bash scripting and used them to automate three practical tasks:
Lightweight monitoring with threshold alerts
Automated SSH key exchange to enable passwordless login
Remote command execution across a fleet of servers
If you’re also early in your DevOps journey, these three scripts will instantly make your workflow more repeatable and scalable.
TL;DR
Loops (
for,while,until) are the backbone of shell automation.I built three scripts that leverage loops:
monitor.sh– checks CPU, memory, and disk usage in a loop and warns on thresholdsssh_key_exchange.sh– copies your SSH public key to many servers (looping over ahosts.txtfile)remote_exec.sh– runs any command on multiple servers and aggregates the output
At the end you’ll also find a Sora video prompt I’m using to document Day 42 visually.
Quick Primer: Loops in Bash
for
Great for iterating over a list of items.
for host in server1 server2 server3; do
echo "Pinging $host"
ping -c1 "$host" >/dev/null && echo "✅ $host is up" || echo "❌ $host is down"
done
while
Perfect when reading from files, streams, or running infinite/polling loops.
while read -r user; do
echo "Creating user: $user"
# useradd "$user"
done < users.txt
until
Runs until a condition becomes true (i.e., loop continues while condition is false).
count=0
until [[ $count -ge 5 ]]; do
echo "Count: $count"
((count++))
sleep 1
done
1) monitor.sh — Mini Monitoring Script (CPU, Mem, Disk)
This script runs in a while loop, checks system vitals, and warns if thresholds are crossed. You can run it in the foreground or background (via nohup / systemd / screen / tmux).
File: monitor.sh
#!/usr/bin/env bash
set -euo pipefail
INTERVAL=${1:-5} # seconds between checks
CPU_THR=${CPU_THR:-85} # % CPU usage threshold
MEM_THR=${MEM_THR:-85} # % Memory usage threshold
DISK_THR=${DISK_THR:-80} # % Disk usage threshold
LOG_DIR=${LOG_DIR:-/var/log/devops}
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/monitor_$(date +%F).log"
log() { echo "$(date +'%F %T') | $*" | tee -a "$LOG_FILE"; }
get_cpu_usage() {
# Quick-and-dirty estimation from /proc/stat (single snapshot)
# For production-grade accuracy, compute deltas between two reads or use mpstat/vmstat.
awk '/^cpu / {usage=($2+$3+$4)*100/($2+$3+$4+$5); printf "%.2f", usage}' /proc/stat
}
get_mem_usage() {
free -m | awk '/Mem:/ {printf "%.2f", $3*100/$2}'
}
check_disk_usage() {
df -P --output=pcent,target | tail -n +2 | while read -r pct mount; do
pct=${pct%%%}
echo "$pct $mount"
done
}
while true; do
CPU=$(get_cpu_usage)
MEM=$(get_mem_usage)
if (( ${CPU%.*} > CPU_THR )); then
log "⚠️ High CPU usage: ${CPU}% (> ${CPU_THR}%)"
else
log "✅ CPU: ${CPU}%"
fi
if (( ${MEM%.*} > MEM_THR )); then
log "⚠️ High Memory usage: ${MEM}% (> ${MEM_THR}%)"
else
log "✅ Memory: ${MEM}%"
fi
# Loop over all mount points
while read -r PCT MNT; do
if (( PCT > DISK_THR )); then
log "⚠️ High Disk usage on $MNT: ${PCT}% (> ${DISK_THR}%)"
else
log "✅ Disk $MNT: ${PCT}%"
fi
done < <(check_disk_usage)
sleep "$INTERVAL"
Done
Run it:
chmod +x monitor.sh sudo ./monitor.sh 10 # check every 10 secondsOptional: pipe alerts to email/Slack/Teams using curl/webhooks.
2) ssh_key_exchange.sh — Distribute Your SSH Key to Many Hosts
This script loops over hosts from hosts.txt, generates an SSH key if you don’t have one, and then runs ssh-copy-id to set up passwordless access.
File: ssh_key_exchange.sh
#!/usr/bin/env bash
set -euo pipefail
HOSTS_FILE=${1:-hosts.txt}
KEY_PATH="${HOME}/.ssh/id_ed25519"
if [[ ! -f "$KEY_PATH" ]]; then
echo "[+] No key found. Generating $KEY_PATH"
ssh-keygen -t ed25519 -N "" -f "$KEY_PATH"
fi
while IFS= read -r host; do
[[ -z "$host" || "$host" =~ ^# ]] && continue
echo "[+] Copying key to $host"
ssh-copy-id -i "$KEY_PATH.pub" "$host"
Done < "$HOSTS_FILE"
Example hosts.txt:
ubuntu@10.0.0.11
root@192.168.1.100
admin@server.example.com
Run it:
chmod +x ssh_key_exchange.sh ./ssh_key_exchange.sh hosts.txt
3) remote_exec.sh — Run Commands on Multiple Servers
This script loops over the same hosts.txt and executes any command you pass. It’s intentionally simple; you can enhance it with parallel execution (parallel or xargs -P), timeouts, retry logic, JSON output, etc.
File: remote_exec.sh
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <hosts-file> [command...]"
exit 1
fi
HOSTS_FILE=$1; shift || true
CMD=${*:-'hostname && uptime'}
while IFS= read -r host; do
[[ -z "$host" || "$host" =~ ^# ]] && continue
echo "\n====== $host ======"
ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" "$CMD" || echo "[!] Failed to run on $host"
Done < "$HOSTS_FILE"
Run it:
chmod +x remote_exec.sh ./remote_exec.sh hosts.txt "uname -a && df -h"
Bonus: Make Them Systemd Services or Cron Jobs
Cron example (run every 5 minutes):
*/5 * * * * /path/to/monitor.sh 5 >> /var/log/devops/monitor_cron.log 2>&1Systemd unit (for monitor.sh):
[Unit] Description=Simple DevOps Monitor [Service] ExecStart=/usr/local/bin/monitor.sh 5 Restart=always User=root [Install] WantedBy=multi-user.target
What I Learned & Next Steps
Loops + files (
while read -r) is the cleanest way to iterate over server lists.Parameterization with env vars (
CPU_THR,DISK_THR, etc.) makes scripts flexible.Error handling with
set -euo pipefailis a must.




