Shell Scripting Redis Database Centric Advanced May 2026

Shell Scripting Database Centric: Redis from the Shell

Interact with Redis from bash using redis-cli — keys, strings, hashes, lists, sets, pub/sub, Lua scripting, distributed locks, rate limiting, and a complete Redis health monitoring script.

Redis is omnipresent in production infrastructure — caching, sessions, queues, pub/sub, rate limiting. Being able to inspect and manipulate Redis from the shell is essential for debugging, operations, and building lightweight coordination tools between services.

BASH
# ── Connection and auth ───────────────────────────────────
redis-cli                            # localhost:6379
redis-cli -h redis.prod.internal -p 6380
redis-cli -h redis.prod.internal -a "${REDIS_PASS}"
redis-cli -u "redis://:${REDIS_PASS}@redis.prod.internal:6380"

# ── Suppress interactive output for scripting ─────────────
redis-cli --no-auth-warning -a "${REDIS_PASS}" PING

# ── Wrapper function ──────────────────────────────────────
redis() {
  redis-cli --no-auth-warning \
    -h "${REDIS_HOST:-localhost}" \
    -p "${REDIS_PORT:-6379}" \
    -a "${REDIS_PASS:-}" \
    -n "${REDIS_DB:-0}" \
    "$@"
}

# ── String operations ─────────────────────────────────────
redis SET mykey "hello"
redis GET mykey
redis SETEX session:abc123 3600 '{"user_id":42}'   # with 1h TTL
redis TTL session:abc123                             # seconds remaining
redis INCR counter:page_views
redis INCRBY counter:bytes_transferred 1024

# ── Hash operations ────────────────────────────────────────
redis HSET user:42 name "Alice" email "alice@example.com" plan pro
redis HGET user:42 email
redis HGETALL user:42
redis HINCRBY user:42 login_count 1

# ── List operations ───────────────────────────────────────
redis LPUSH queue:emails '{"to":"user@example.com"}'
redis RPOP queue:emails        # dequeue from right
redis LLEN queue:emails        # queue depth
BASH
# ── Distributed lock (mutex) ──────────────────────────────
acquire_lock() {
  local key="lock:${1}"
  local ttl="${2:-30}"    # seconds
  local token="${HOSTNAME}:$$:$(date +%s%N)"

  # SET NX EX: set only if not exists, with TTL
  result=$(redis SET "${key}" "${token}" NX EX "${ttl}")
  [[ "${result}" == "OK" ]] && echo "${token}" && return 0
  return 1  # lock already held
}

release_lock() {
  local key="lock:${1}" token="${2}"
  # Lua: only delete if the token matches (atomic check-and-delete)
  redis EVAL "if redis.call('GET',KEYS[1])==ARGV[1] then
    return redis.call('DEL',KEYS[1]) else return 0 end" \
    1 "${key}" "${token}"
}

# Usage: serialise a cron job
if TOKEN=$(acquire_lock "daily_report" 300); then
  trap "release_lock daily_report ${TOKEN}" EXIT
  generate_daily_report
else
  echo "Another instance is running, skipping"
  exit 0
fi

# ── Rate limiting: sliding window counter ─────────────────
check_rate_limit() {
  local key="ratelimit:${1}"
  local max="${2:-100}"   # max requests
  local window="${3:-60}" # per N seconds

  count=$(redis EVAL "
    local current = redis.call('INCR', KEYS[1])
    if current == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end
    return current
  " 1 "${key}" "${window}")

  (( count > max )) && return 1  # rate limited
  return 0
}
BASH
#!/usr/bin/env bash
# redis_health.sh — Comprehensive Redis health check

set -euo pipefail
redis() { redis-cli --no-auth-warning -a "${REDIS_PASS:-}" "$@" 2>/dev/null; }

# ── Connectivity ──────────────────────────────────────────
redis PING | grep -q PONG || { echo "CRITICAL: Redis not responding"; exit 1; }

# ── Memory usage ─────────────────────────────────────────
MEM_USED=$(redis INFO memory | awk -F: '/^used_memory_human/{print $2}' | tr -d '\r')
MEM_PEAK=$(redis INFO memory | awk -F: '/^used_memory_peak_human/{print $2}' | tr -d '\r')
MEM_PCT=$(redis INFO memory | awk -F: '/^mem_fragmentation_ratio/{printf "%.0f", $2*100}')
printf "  Memory: %s used / peak %s (frag ratio: %s%%)\n" \
  "${MEM_USED}" "${MEM_PEAK}" "${MEM_PCT}"

# ── Connected clients ─────────────────────────────────────
CLIENTS=$(redis INFO clients | awk -F: '/^connected_clients/{print $2}' | tr -d '\r')
printf "  Clients: %s connected\n" "${CLIENTS}"

# ── Key statistics ────────────────────────────────────────
KEYS=$(redis DBSIZE)
HITS=$(redis INFO stats | awk -F: '/^keyspace_hits/{print $2}' | tr -d '\r')
MISSES=$(redis INFO stats | awk -F: '/^keyspace_misses/{print $2}' | tr -d '\r')
TOTAL=$(( HITS + MISSES ))
HIT_RATE=$(( TOTAL > 0 ? HITS * 100 / TOTAL : 0 ))
printf "  Keys: %s | Hit rate: %s%%\n" "${KEYS}" "${HIT_RATE}"

# ── Replication status ────────────────────────────────────
ROLE=$(redis INFO replication | awk -F: '/^role/{print $2}' | tr -d '\r')
printf "  Role: %s\n" "${ROLE}"
[[ "${ROLE}" == "slave" ]] && {
  LAG=$(redis INFO replication | awk -F: '/^master_last_io_seconds_ago/{print $2}')
  (( LAG > 10 )) && echo "  WARN: Replication lag ${LAG}s"
}

# ── Top 5 largest keys ────────────────────────────────────
echo "  Largest keys:"
redis --bigkeys 2>/dev/null | grep '^Biggest' | head -5 | \
  awk '{printf "    %-30s %s bytes\n", $NF, $(NF-2)}'
redis-cli — health check
vriddh@prod-01:~/scripts$./redis_health.sh
Memory: 842.12M used / peak 1.10G (frag ratio: 112%)
Clients: 84 connected
Keys: 142831 | Hit rate: 94%
Role: master
Largest keys:
cache:homepage_v2 2487312 bytes
✔ Redis from shell — Use SET key value NX EX ttl for atomic lock acquisition. Use Lua scripts via EVAL for atomic check-and-act operations that must not race. Always use --no-auth-warning when passing -a to suppress the security warning in logs. Parse redis-cli INFO section output with AWK using -F: and pattern matching. Use redis-cli --pipe for bulk import via the Redis Inline Protocol.