Created
April 14, 2026 15:02
-
-
Save NQevxvEtg/307caf156966d0da10ec837bb5d30135 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # networkmonitor.sh - Per-process network bandwidth monitor (nethogs alternative) | |
| # | |
| # Samples 'ss -tupni' byte counters (bytes_acked / bytes_received) per connection, | |
| # maps them to PIDs, and computes upload/download rates. | |
| # | |
| # Usage: ./networkmonitor.sh [interval_seconds] | |
| # Run as root (sudo) for full visibility of all processes. | |
| INTERVAL=${1:-2} | |
| if ! command -v ss &>/dev/null; then | |
| echo "Error: 'ss' command not found." >&2 | |
| exit 1 | |
| fi | |
| if (( EUID != 0 )); then | |
| echo "Note: Run as root (sudo) to see all processes." >&2 | |
| fi | |
| declare -A _CTX _CRX _CN _PTX _PRX _PN | |
| parse_ss() { | |
| _CTX=(); _CRX=(); _CN=() | |
| local pid="" name="" tx=0 rx=0 | |
| while IFS= read -r line; do | |
| if [[ "$line" =~ ^(tcp|udp) ]]; then | |
| [[ -n "$pid" ]] && { | |
| _CTX[$pid]=$(( ${_CTX[$pid]:-0} + tx )) | |
| _CRX[$pid]=$(( ${_CRX[$pid]:-0} + rx )) | |
| _CN[$pid]="$name" | |
| } | |
| tx=0; rx=0; pid="" | |
| [[ "$line" =~ users:\(\(\"([^\"]+)\",pid=([0-9]+) ]] && { | |
| name="${BASH_REMATCH[1]}"; pid="${BASH_REMATCH[2]}" | |
| } | |
| else | |
| [[ "$line" =~ bytes_acked:([0-9]+) ]] && tx="${BASH_REMATCH[1]}" | |
| [[ "$line" =~ bytes_received:([0-9]+) ]] && rx="${BASH_REMATCH[1]}" | |
| fi | |
| done < <(ss -tupni 2>/dev/null) | |
| [[ -n "$pid" ]] && { | |
| _CTX[$pid]=$(( ${_CTX[$pid]:-0} + tx )) | |
| _CRX[$pid]=$(( ${_CRX[$pid]:-0} + rx )) | |
| _CN[$pid]="$name" | |
| } | |
| } | |
| fmt_rate() { | |
| awk -v b="$1" 'BEGIN { | |
| if (b >= 1073741824) printf "%.1f GB/s", b / 1073741824 | |
| else if (b >= 1048576) printf "%.1f MB/s", b / 1048576 | |
| else if (b >= 1024) printf "%.1f KB/s", b / 1024 | |
| else printf "%d B/s", b | |
| }' | |
| } | |
| fmt_bytes() { | |
| awk -v b="$1" 'BEGIN { | |
| if (b >= 1073741824) printf "%.1f GB", b / 1073741824 | |
| else if (b >= 1048576) printf "%.1f MB", b / 1048576 | |
| else if (b >= 1024) printf "%.1f KB", b / 1024 | |
| else printf "%d B", b | |
| }' | |
| } | |
| copy_curr_to_prev() { | |
| _PTX=(); _PRX=(); _PN=() | |
| for p in "${!_CTX[@]}"; do | |
| _PTX[$p]=${_CTX[$p]} | |
| _PRX[$p]=${_CRX[$p]} | |
| _PN[$p]=${_CN[$p]} | |
| done | |
| } | |
| total_net_dev() { | |
| awk 'NR>2 && $1 !~ /lo:/ { | |
| gsub(/:/, "", $1) | |
| rx += $2; tx += $10 | |
| } END { print rx, tx }' /proc/net/dev | |
| } | |
| trap 'tput cnorm 2>/dev/null; echo; exit 0' INT TERM | |
| tput civis 2>/dev/null | |
| parse_ss | |
| copy_curr_to_prev | |
| read prev_total_rx prev_total_tx < <(total_net_dev) | |
| while true; do | |
| sleep "$INTERVAL" | |
| parse_ss | |
| read curr_total_rx curr_total_tx < <(total_net_dev) | |
| clear | |
| printf "\n Network Monitor — %s (interval: %ds)\n\n" "$(date '+%H:%M:%S')" "$INTERVAL" | |
| printf " %-7s %-20s %12s %12s\n" "PID" "PROCESS" "UPLOAD" "DOWNLOAD" | |
| printf " %55s\n" "" | tr ' ' ─ | |
| tmpf=$(mktemp) | |
| for p in "${!_CTX[@]}"; do | |
| dtx=$(( ${_CTX[$p]:-0} - ${_PTX[$p]:-0} )) | |
| drx=$(( ${_CRX[$p]:-0} - ${_PRX[$p]:-0} )) | |
| (( dtx < 0 )) && dtx=0 | |
| (( drx < 0 )) && drx=0 | |
| rtx=$(( dtx / INTERVAL )) | |
| rrx=$(( drx / INTERVAL )) | |
| echo "$((dtx + drx)) $p $rtx $rrx" >> "$tmpf" | |
| done | |
| has_output=false | |
| while IFS=' ' read -r _ p rtx rrx; do | |
| [[ -n "$p" ]] || continue | |
| (( rtx > 0 || rrx > 0 )) || continue | |
| has_output=true | |
| nm="${_CN[$p]:-${_PN[$p]:-unknown}}" | |
| printf " %-7s %-20s %12s %12s\n" \ | |
| "$p" "${nm:0:20}" "$(fmt_rate "$rtx")" "$(fmt_rate "$rrx")" | |
| done < <(sort -rn "$tmpf") | |
| rm -f "$tmpf" | |
| [[ "$has_output" == "false" ]] && printf " (no active TCP/UDP traffic)\n" | |
| printf "\n %55s\n" "" | tr ' ' ─ | |
| total_sys_tx=$(( curr_total_tx - prev_total_tx )) | |
| total_sys_rx=$(( curr_total_rx - prev_total_rx )) | |
| printf " %-28s %12s %12s\n" "SYSTEM TOTAL" \ | |
| "$(fmt_rate "$((total_sys_tx / INTERVAL))")" \ | |
| "$(fmt_rate "$((total_sys_rx / INTERVAL))")" | |
| prev_total_tx=$curr_total_tx | |
| prev_total_rx=$curr_total_rx | |
| copy_curr_to_prev | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment