#!/usr/bin/env bash # # List open (listened) ports in Markdown or JSON format. # # Modified by: Ismo Vuorinen 2020, 2025 # Originally from: https://www.commandlinefu.com/commands/view/8951 # Original author: https://www.commandlinefu.com/commands/by/wickedcpj set -euo pipefail FORMAT="markdown" # Function to print help message print_help() { cat << EOF Usage: $(basename "$0") [OPTIONS] List open (listened) ports in a formatted table (Markdown) or JSON. Options: --json Output results in JSON format instead of Markdown --help Show this help message Examples: $(basename "$0") # List open ports as a Markdown table $(basename "$0") --json # List open ports in JSON format EOF exit 0 } # Function to print the Markdown table header print_header() { echo "| User | Command | PID | Port |" echo "|------------------|----------------------------|----------|---------|" } # Function to list open ports using lsof list_open_ports_lsof() { lsof -i -P -n -sTCP:LISTEN +c 0 2> /dev/null | awk ' NR > 1 { port = $9 sub(/.*:/, "", port) # Extract port number printf "| %-16s | %-26s | %-8s | %-7s |\n", substr($3, 1, 16), substr($1, 1, 26), substr($2, 1, 8), port } ' | sort -k3,3n | uniq } # Function to list open ports using ss (alternative) list_open_ports_ss() { ss -ltpn 2> /dev/null | awk ' NR > 1 { split($5, addr, ":") port = addr[length(addr)] user = $1 cmd = $7 sub(/users:\(\(/, "", cmd) # Cleanup command sub(/\)\)/, "", cmd) pid = "-" match(cmd, /pid=([0-9]+)/, m) if (m[1] != "") pid = m[1] printf "| %-16s | %-26s | %-8s | %-7s |\n", substr(user, 1, 16), substr(cmd, 1, 26), substr(pid, 1, 8), port } ' | sort -k3,3n | uniq } # Function to print JSON output list_open_ports_json() { if command -v lsof &> /dev/null; then lsof -i -P -n -sTCP:LISTEN +c 0 2> /dev/null | awk ' NR > 1 { port = $9 sub(/.*:/, "", port) # Extract port number printf "{\"user\": \"%s\", \"command\": \"%s\", \"pid\": \"%s\", \"port\": \"%s\"},\n", $3, $1, $2, port } ' | sort -k3,3n | uniq | sed '$ s/,$//' elif command -v ss &> /dev/null; then ss -ltpn 2> /dev/null | awk ' NR > 1 { split($5, addr, ":") port = addr[length(addr)] user = $1 cmd = $7 sub(/users:\(\(/, "", cmd) sub(/\)\)/, "", cmd) pid = "-" match(cmd, /pid=([0-9]+)/, m) if (m[1] != "") pid = m[1] printf "{\"user\": \"%s\", \"command\": \"%s\", \"pid\": \"%s\", \"port\": \"%s\"},\n", user, cmd, pid, port } ' | sort -k3,3n | uniq | sed '$ s/,$//' else echo "[]" fi } # Function to determine available command list_open_ports() { if [[ "$FORMAT" == "json" ]]; then echo "[" list_open_ports_json echo "]" else print_header if command -v lsof &> /dev/null; then list_open_ports_lsof elif command -v ss &> /dev/null; then list_open_ports_ss else echo "**Error:** Neither 'lsof' nor 'ss' is available." exit 1 fi fi } # Main function main() { case "${1:-}" in --json) FORMAT="json" ;; --help) print_help ;; "") ;; *) echo "Unknown option: $1" print_help ;; esac list_open_ports echo "" } main "$@"