@@ 0,0 1,382 @@
+#!/usr/bin/env bash
+# ram — pretty visual breakdown of RAM + swap usage on this machine
+# usage: ram [-n SIZE] [-p PAGE] [-a]
+# -n N rows per page in process tables (default 15)
+# -p N page number, 1-indexed (default 1)
+# -a show all rows (disables pagination)
+# header/breakdown sections always show; only process tables paginate
+
+set -u
+
+TOPN="${TOPN:-15}"
+PAGE="${PAGE:-1}"
+ALL=0
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -n) TOPN="$2"; shift 2 ;;
+ -p) PAGE="$2"; shift 2 ;;
+ -a|--all) ALL=1; shift ;;
+ -h|--help)
+ sed -n '2,7p' "$0" | sed 's/^# \{0,1\}//'
+ exit 0
+ ;;
+ *) shift ;;
+ esac
+done
+
+# sanity
+case "$TOPN" in (''|*[!0-9]*) TOPN=15 ;; esac
+case "$PAGE" in (''|*[!0-9]*) PAGE=1 ;; esac
+[ "$PAGE" -lt 1 ] && PAGE=1
+[ "$TOPN" -lt 1 ] && TOPN=15
+OFFSET=$(( (PAGE - 1) * TOPN ))
+
+# ---- colors -----------------------------------------------------------------
+if [ -t 1 ]; then
+ C_RESET=$'\033[0m'
+ C_BOLD=$'\033[1m'
+ C_DIM=$'\033[2m'
+ C_RED=$'\033[38;5;203m'
+ C_ORG=$'\033[38;5;215m'
+ C_YEL=$'\033[38;5;221m'
+ C_GRN=$'\033[38;5;114m'
+ C_CYN=$'\033[38;5;111m'
+ C_BLU=$'\033[38;5;75m'
+ C_MAG=$'\033[38;5;176m'
+ C_GRY=$'\033[38;5;244m'
+else
+ C_RESET= C_BOLD= C_DIM= C_RED= C_ORG= C_YEL= C_GRN= C_CYN= C_BLU= C_MAG= C_GRY=
+fi
+
+# ---- helpers ----------------------------------------------------------------
+human() {
+ # human-readable from KiB
+ python3 -c "
+v=$1
+u=['KiB','MiB','GiB','TiB']
+i=0
+while v>=1024 and i<len(u)-1:
+ v/=1024; i+=1
+print(f'{v:.1f} {u[i]}')
+"
+}
+
+bar() {
+ # bar percent width color
+ local pct="$1" width="${2:-40}" color="${3:-$C_GRN}"
+ python3 - "$pct" "$width" "$color" "$C_GRY" "$C_RESET" <<'PY'
+import sys
+pct=float(sys.argv[1]); w=int(sys.argv[2])
+color=sys.argv[3]; gry=sys.argv[4]; rst=sys.argv[5]
+filled=int(round(w*pct/100))
+filled=max(0,min(w,filled))
+print(f"{color}{'█'*filled}{gry}{'░'*(w-filled)}{rst}", end='')
+PY
+}
+
+pct_color() {
+ local p="$1"
+ python3 -c "p=$p
+print('"$C_RED"' if p>=90 else ('"$C_ORG"' if p>=75 else ('"$C_YEL"' if p>=50 else '"$C_GRN"')))"
+}
+
+hr() { printf "${C_GRY}%s${C_RESET}\n" "────────────────────────────────────────────────────────────────────────────────"; }
+
+# ---- parse /proc/meminfo ----------------------------------------------------
+declare -A MI
+while IFS=':' read -r k v; do
+ v=$(echo "$v" | awk '{print $1}')
+ MI["$k"]="$v"
+done < /proc/meminfo
+
+MEM_TOTAL=${MI[MemTotal]}
+MEM_FREE=${MI[MemFree]}
+MEM_AVAIL=${MI[MemAvailable]}
+BUFFERS=${MI[Buffers]:-0}
+CACHED=${MI[Cached]:-0}
+SRECLAIM=${MI[SReclaimable]:-0}
+SHMEM=${MI[Shmem]:-0}
+SLAB=${MI[Slab]:-0}
+SUNRECLAIM=${MI[SUnreclaim]:-0}
+KSTACK=${MI[KernelStack]:-0}
+PAGETABLES=${MI[PageTables]:-0}
+ANON=${MI[AnonPages]:-0}
+MAPPED=${MI[Mapped]:-0}
+SWAP_TOTAL=${MI[SwapTotal]:-0}
+SWAP_FREE=${MI[SwapFree]:-0}
+SWAP_CACHED=${MI[SwapCached]:-0}
+DIRTY=${MI[Dirty]:-0}
+
+# Effective "used" = total - available (matches what `free` reports as used)
+MEM_USED=$(( MEM_TOTAL - MEM_AVAIL ))
+SWAP_USED=$(( SWAP_TOTAL - SWAP_FREE ))
+
+mem_pct=$(python3 -c "print(f'{$MEM_USED/$MEM_TOTAL*100:.1f}')")
+swap_pct=$(python3 -c "t=$SWAP_TOTAL; print(f'{0 if t==0 else $SWAP_USED/t*100:.1f}')")
+
+# ---- header -----------------------------------------------------------------
+printf "\n ${C_BOLD}MEMORY${C_RESET} ${C_DIM}$(uname -n) — $(date '+%Y-%m-%d %H:%M:%S')${C_RESET}\n\n"
+
+mc=$(pct_color "$mem_pct")
+sc=$(pct_color "$swap_pct")
+
+printf " ${C_BOLD}RAM ${C_RESET} "
+bar "$mem_pct" 50 "$mc"
+printf " ${mc}%5s%%${C_RESET} ${C_BOLD}%s${C_RESET} / %s ${C_DIM}avail %s${C_RESET}\n" \
+ "$mem_pct" "$(human $MEM_USED)" "$(human $MEM_TOTAL)" "$(human $MEM_AVAIL)"
+
+printf " ${C_BOLD}SWAP${C_RESET} "
+bar "$swap_pct" 50 "$sc"
+printf " ${sc}%5s%%${C_RESET} ${C_BOLD}%s${C_RESET} / %s ${C_DIM}cached %s${C_RESET}\n" \
+ "$swap_pct" "$(human $SWAP_USED)" "$(human $SWAP_TOTAL)" "$(human $SWAP_CACHED)"
+
+# ---- kernel / cache breakdown -----------------------------------------------
+echo
+printf " ${C_BOLD}Where it goes${C_RESET}\n"
+hr
+printf " %-22s %12s %s\n" "category" "size" "what it is"
+hr
+
+print_row() {
+ local label="$1" kib="$2" desc="$3" color="${4:-$C_CYN}"
+ printf " ${color}%-22s${C_RESET} %12s ${C_DIM}%s${C_RESET}\n" "$label" "$(human $kib)" "$desc"
+}
+
+print_row "AnonPages" "$ANON" "process heap/stack (the real working set)" "$C_RED"
+print_row "Mapped" "$MAPPED" "files mmap'd into processes (libraries, etc)" "$C_ORG"
+print_row "Shmem" "$SHMEM" "tmpfs + shared memory (/dev/shm, /tmp, IPC)" "$C_MAG"
+print_row "Cached" "$CACHED" "page cache from disk reads (reclaimable)" "$C_GRN"
+print_row "Buffers" "$BUFFERS" "block-device buffers (reclaimable)" "$C_GRN"
+print_row "Slab" "$SLAB" "kernel object cache (dentries, inodes, …)" "$C_YEL"
+print_row " ↳ SReclaim" "$SRECLAIM" "reclaimable portion of slab" "$C_GRN"
+print_row " ↳ SUnreclaim" "$SUNRECLAIM" "unreclaimable slab — kernel actually needs it" "$C_RED"
+print_row "KernelStack" "$KSTACK" "kernel stacks for live threads" "$C_CYN"
+print_row "PageTables" "$PAGETABLES" "MMU page tables (grows with #procs * RSS)" "$C_CYN"
+print_row "Dirty" "$DIRTY" "modified pages waiting to be written to disk" "$C_YEL"
+
+# ---- /tmp + /dev/shm --------------------------------------------------------
+echo
+printf " ${C_BOLD}tmpfs occupancy${C_RESET} ${C_DIM}(lives in RAM — Shmem above includes this)${C_RESET}\n"
+hr
+python3 - <<'PY'
+import subprocess
+out = subprocess.run(['df','-k','--output=target,size,used,pcent','-t','tmpfs'],
+ capture_output=True, text=True).stdout.strip().splitlines()
+def h(k):
+ v=float(k); u='K'
+ if v>=1024: v/=1024; u='M'
+ if v>=1024: v/=1024; u='G'
+ return f"{v:.1f}{u}"
+rows=[]
+for line in out[1:]:
+ parts=line.split()
+ if len(parts)<4: continue
+ target, size, used, pcent = parts[0], parts[1], parts[2], parts[3]
+ # filter trivial / credential mounts
+ if target.startswith('/run/credentials/'): continue
+ if int(used)==0: continue
+ rows.append((int(used), target, h(int(size)+0.0), h(int(used)+0.0), pcent))
+rows.sort(reverse=True)
+print(f" {'mount':<32} {'size':>8} {'used':>8} {'use%':>6}")
+for _,t,s,u,p in rows:
+ print(f" {t:<32} {s:>8} {u:>8} {p:>6}")
+PY
+
+# ---- top processes by RSS ---------------------------------------------------
+echo
+printf " ${C_BOLD}Processes by RSS${C_RESET} ${C_DIM}(resident in RAM)${C_RESET}\n"
+hr
+printf " ${C_DIM}%-7s %-10s %9s %9s %6s %s${C_RESET}\n" "PID" "USER" "RSS" "VSZ" "%MEM" "COMMAND"
+python3 - "$TOPN" "$OFFSET" "$ALL" "$C_RESET" "$C_DIM" "$C_RED" "$C_ORG" "$C_YEL" "$C_GRN" <<'PY'
+import os, sys, pwd, subprocess
+n=int(sys.argv[1]); off=int(sys.argv[2]); show_all=int(sys.argv[3])
+rst=sys.argv[4]; dim=sys.argv[5]
+r=sys.argv[6]; o=sys.argv[7]; y=sys.argv[8]; g=sys.argv[9]
+def h(k):
+ v=float(k); u='K'
+ if v>=1024: v/=1024; u='M'
+ if v>=1024: v/=1024; u='G'
+ return f"{v:.1f}{u}"
+out=subprocess.run(['ps','-eo','pid,user:12,rss,vsz,pmem,args','--sort=-rss','--no-headers'],
+ capture_output=True, text=True).stdout.splitlines()
+total=len(out)
+rows = out if show_all else out[off:off+n]
+if not rows and off>0:
+ print(f" {dim}(no rows on this page — total {total}){rst}")
+for line in rows:
+ parts=line.split(None, 5)
+ if len(parts)<6: continue
+ pid,user,rss,vsz,pmem,args=parts
+ pmemf=float(pmem)
+ col=g
+ if pmemf>=10: col=r
+ elif pmemf>=5: col=o
+ elif pmemf>=2: col=y
+ if len(args)>62: args=args[:59]+'...'
+ print(f" {pid:<7} {user:<10} {h(rss):>9} {h(vsz):>9} {col}{pmem:>5}%{rst} {args}")
+if not show_all and total>n:
+ pages=(total+n-1)//n
+ cur=off//n + 1
+ lo=off+1; hi=min(off+n, total)
+ print(f" {dim}page {cur}/{pages} • rows {lo}-{hi} of {total}{rst}")
+PY
+
+# ---- top processes by swap --------------------------------------------------
+echo
+printf " ${C_BOLD}Processes by SWAP${C_RESET} ${C_DIM}(VmSwap from /proc — needs read perms)${C_RESET}\n"
+hr
+printf " ${C_DIM}%-7s %-10s %9s %9s %s${C_RESET}\n" "PID" "USER" "SWAP" "RSS" "COMMAND"
+
+python3 - "$TOPN" "$OFFSET" "$ALL" "$C_DIM" "$C_RESET" <<'PY'
+import os, sys, pwd
+n=int(sys.argv[1]); off=int(sys.argv[2]); show_all=int(sys.argv[3])
+dim=sys.argv[4]; rst=sys.argv[5]
+rows=[]
+for pid in os.listdir('/proc'):
+ if not pid.isdigit(): continue
+ try:
+ with open(f'/proc/{pid}/status') as f:
+ d={}
+ for line in f:
+ if ':' not in line: continue
+ k,v=line.split(':',1)
+ d[k]=v.strip()
+ sw=int(d.get('VmSwap','0 kB').split()[0])
+ rss=int(d.get('VmRSS','0 kB').split()[0])
+ if sw==0: continue
+ uid=int(d.get('Uid','0').split()[0])
+ try: user=pwd.getpwuid(uid).pw_name
+ except: user=str(uid)
+ name=d.get('Name','?')
+ try:
+ with open(f'/proc/{pid}/cmdline','rb') as f:
+ cmd=f.read().replace(b'\x00',b' ').decode('utf-8','replace').strip()
+ if not cmd: cmd=name
+ except: cmd=name
+ rows.append((sw,rss,pid,user,cmd))
+ except (FileNotFoundError, PermissionError, ProcessLookupError):
+ continue
+
+rows.sort(reverse=True)
+def h(k):
+ v=float(k); u='K'
+ if v>=1024: v/=1024; u='M'
+ if v>=1024: v/=1024; u='G'
+ return f"{v:.1f}{u}"
+
+total=len(rows)
+page = rows if show_all else rows[off:off+n]
+if not page and off>0:
+ print(f" {dim}(no rows on this page — total {total}){rst}")
+for sw,rss,pid,user,cmd in page:
+ if len(cmd)>62: cmd=cmd[:59]+'...'
+ print(f" {pid:<7} {user:<10} {h(sw):>9} {h(rss):>9} {cmd}")
+
+if not rows:
+ print(" (no per-process swap data available — try with sudo)")
+elif not show_all and total>n:
+ pages=(total+n-1)//n
+ cur=off//n + 1
+ lo=off+1; hi=min(off+n, total)
+ print(f" {dim}page {cur}/{pages} • rows {lo}-{hi} of {total}{rst}")
+PY
+
+# ---- per-user totals --------------------------------------------------------
+echo
+printf " ${C_BOLD}By user${C_RESET} ${C_DIM}(summed RSS / SWAP)${C_RESET}\n"
+hr
+python3 - "$TOPN" "$OFFSET" "$ALL" "$C_DIM" "$C_RESET" <<'PY'
+import os, sys, pwd
+n=int(sys.argv[1]); off=int(sys.argv[2]); show_all=int(sys.argv[3])
+dim=sys.argv[4]; rst=sys.argv[5]
+agg={}
+for pid in os.listdir('/proc'):
+ if not pid.isdigit(): continue
+ try:
+ with open(f'/proc/{pid}/status') as f:
+ d={}
+ for line in f:
+ if ':' not in line: continue
+ k,v=line.split(':',1)
+ d[k]=v.strip()
+ rss=int(d.get('VmRSS','0 kB').split()[0])
+ sw=int(d.get('VmSwap','0 kB').split()[0])
+ uid=int(d.get('Uid','0').split()[0])
+ try: user=pwd.getpwuid(uid).pw_name
+ except: user=str(uid)
+ a=agg.setdefault(user,[0,0,0])
+ a[0]+=rss; a[1]+=sw; a[2]+=1
+ except (FileNotFoundError, PermissionError, ProcessLookupError):
+ continue
+
+def h(k):
+ v=float(k); u='K'
+ if v>=1024: v/=1024; u='M'
+ if v>=1024: v/=1024; u='G'
+ return f"{v:.1f}{u}"
+
+rows=sorted(agg.items(), key=lambda x:-x[1][0])
+total=len(rows)
+page = rows if show_all else rows[off:off+n]
+print(f" {'user':<14} {'procs':>6} {'RSS':>10} {'SWAP':>10}")
+if not page and off>0:
+ print(f" {dim}(no rows on this page — total {total}){rst}")
+for u,(rss,sw,nproc) in page:
+ print(f" {u:<14} {nproc:>6} {h(rss):>10} {h(sw):>10}")
+if not show_all and total>n:
+ pages=(total+n-1)//n
+ cur=off//n + 1
+ lo=off+1; hi=min(off+n, total)
+ print(f" {dim}page {cur}/{pages} • rows {lo}-{hi} of {total}{rst}")
+PY
+
+# ---- by process name (grouped, e.g. chrome) ---------------------------------
+echo
+printf " ${C_BOLD}Grouped by command${C_RESET} ${C_DIM}(same comm name summed — catches forky apps)${C_RESET}\n"
+hr
+python3 - "$TOPN" "$OFFSET" "$ALL" "$C_DIM" "$C_RESET" <<'PY'
+import os, sys
+n=int(sys.argv[1]); off=int(sys.argv[2]); show_all=int(sys.argv[3])
+dim=sys.argv[4]; rst=sys.argv[5]
+agg={}
+for pid in os.listdir('/proc'):
+ if not pid.isdigit(): continue
+ try:
+ with open(f'/proc/{pid}/status') as f:
+ d={}
+ for line in f:
+ if ':' not in line: continue
+ k,v=line.split(':',1)
+ d[k]=v.strip()
+ rss=int(d.get('VmRSS','0 kB').split()[0])
+ sw=int(d.get('VmSwap','0 kB').split()[0])
+ name=d.get('Name','?')
+ if rss==0 and sw==0: continue
+ a=agg.setdefault(name,[0,0,0])
+ a[0]+=rss; a[1]+=sw; a[2]+=1
+ except (FileNotFoundError, PermissionError, ProcessLookupError):
+ continue
+
+def h(k):
+ v=float(k); u='K'
+ if v>=1024: v/=1024; u='M'
+ if v>=1024: v/=1024; u='G'
+ return f"{v:.1f}{u}"
+
+rows=sorted(agg.items(), key=lambda x:-(x[1][0]+x[1][1]))
+total=len(rows)
+page = rows if show_all else rows[off:off+n]
+print(f" {'command':<28} {'procs':>6} {'RSS':>10} {'SWAP':>10}")
+if not page and off>0:
+ print(f" {dim}(no rows on this page — total {total}){rst}")
+for name,(rss,sw,np) in page:
+ print(f" {name[:28]:<28} {np:>6} {h(rss):>10} {h(sw):>10}")
+if not show_all and total>n:
+ pages=(total+n-1)//n
+ cur=off//n + 1
+ lo=off+1; hi=min(off+n, total)
+ print(f" {dim}page {cur}/{pages} • rows {lo}-{hi} of {total}{rst}")
+PY
+
+echo
+printf " ${C_DIM}tip: ${C_RESET}${C_DIM}ram -n SIZE -p PAGE • ram -a shows all rows • sudo ram reads every process${C_RESET}\n\n"