#!/usr/bin/env bash # port_kill — Mata los procesos que escuchan en un puerto TCP dado. port_kill() { local port="${1:-}" local signal="${2:-TERM}" # Validar puerto if [[ -z "$port" ]] || ! [[ "$port" =~ ^[0-9]+$ ]] || (( port < 1 || port > 65535 )); then echo "ERROR: puerto invalido: '$port' (debe ser 1-65535)" >&2 return 2 fi # Verificar herramienta disponible local tool="" if command -v lsof &>/dev/null; then tool="lsof" elif command -v fuser &>/dev/null; then tool="fuser" else echo "ERROR: se requiere lsof o fuser (ninguno disponible)" >&2 return 5 fi # Obtener PIDs local pids=() if [[ "$tool" == "lsof" ]]; then mapfile -t pids < <(lsof -ti "tcp:${port}" -sTCP:LISTEN 2>/dev/null) else mapfile -t pids < <(fuser -n tcp "${port}" 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+$') fi if (( ${#pids[@]} == 0 )); then echo "NO_PROCESS port=${port}" return 0 fi # Matar cada PID local permission_denied=0 for pid in "${pids[@]}"; do [[ -z "$pid" ]] && continue if kill "-${signal}" "$pid" 2>/dev/null; then echo "KILLED pid=${pid} signal=${signal} port=${port}" else echo "PERMISSION_DENIED pid=${pid}" >&2 permission_denied=1 fi done if (( permission_denied )); then return 4 fi # Esperar 2s y verificar sleep 2 local remaining=() if [[ "$tool" == "lsof" ]]; then mapfile -t remaining < <(lsof -ti "tcp:${port}" -sTCP:LISTEN 2>/dev/null) else mapfile -t remaining < <(fuser -n tcp "${port}" 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+$') fi if (( ${#remaining[@]} == 0 )); then return 0 fi # Segundo intento con KILL si signal != KILL if [[ "$signal" != "KILL" && "$signal" != "9" ]]; then for pid in "${remaining[@]}"; do [[ -z "$pid" ]] && continue kill -KILL "$pid" 2>/dev/null && echo "KILLED pid=${pid} signal=KILL port=${port}" done sleep 1 local still=() if [[ "$tool" == "lsof" ]]; then mapfile -t still < <(lsof -ti "tcp:${port}" -sTCP:LISTEN 2>/dev/null) else mapfile -t still < <(fuser -n tcp "${port}" 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+$') fi if (( ${#still[@]} > 0 )); then echo "ERROR: puerto ${port} sigue ocupado tras SIGKILL" >&2 return 3 fi else echo "ERROR: puerto ${port} sigue ocupado tras SIGKILL" >&2 return 3 fi return 0 }