igozhang

——

    网络聚合命令

    网络配置命令(IP/网关/DNS/NTP/聚合)

    逻辑:备份 → 写配置文件 → NTP → 重启网络生效 → 输出。不读旧配置内容。
    网口IFACE='*' 自动探测(优先 bond/team > eth > ens/enp,排除 docker/k8s 等)。
    Ubuntu netplan:备份后重写原 yaml(含该网口的文件,否则取第一个),删除其余 yaml 避免冲突。
    CentOS/Rocky:写最小 ifcfg(仅 IP/网关/DNS)→ 配 NTP → 生效。CentOS 7 优先 nmcli device reapply 读 ifcfg;无 NM 时用 restart network;兜底用 ip 命令。
    DNS:默认留空,空时自动使用 GW 作为 DNS。
    说明:以下四条命令均 ≤2000 字符,可直接复制;IP/GW 各一行,其余参数共一行。

    1. Ubuntu/Debian 单网口(netplan)

    sudo bash <<'EOF'
    IP='10.10.10.10'
    GW='10.10.10.254'
    DNS='';NTP='';MASK='24';IF='*'
    [ -z "$DNS" ]&&DNS=$GW
    P(){ ip -br -4 a|awk '$1!~"^(lo|docker|veth|kube|cni|br-|flannel)"&&$2=="UP"&&$3~/\//{print($1~/^bond/?100:$1~/^eth/?90:$1~/^ens/?80:10),$1}'|sort -rn|head -1|awk '{print $2}'; }
    [ "$IF"="*" ]&&IF=$(P);[ -n "$IF" ]||{ echo FAIL:iface;exit 1; }
    echo "CFG: $IF $IP/$MASK gw=$GW dns=$DNS${NTP:+ ntp=$NTP}"
    D=/root/nb-$(date +%Y%m%d%H%M%S);mkdir -p $D;cp -a /etc/netplan $D/ 2>/dev/null&&echo OK:backup:$D||echo FAIL:backup
    shopt -s nullglob;Y=(/etc/netplan/*.yaml);T=$(printf '%s\n' "${Y[@]}"|sort|head -1)
    for f in "${Y[@]}";do grep -q " $IF:" "$f"&&T=$f;done;[ -z "$T" ]&&T=/etc/netplan/01-netcfg.yaml
    R=$(grep -h renderer: $D/netplan/*.yaml 2>/dev/null|awk '{print $2}'|head -1);R=${R:-networkd}
    for f in /etc/netplan/*.yaml;do [ "$f"!="$T" ]&&rm -f "$f";done;shopt -u nullglob
    cat>$T<<E
    network:
      version: 2
      renderer: $R
      ethernets:
        $IF:
          dhcp4: no
          addresses: [$IP/$MASK]
          routes: [{to: default, via: $GW}]
          nameservers: {addresses: [$DNS]}
    E
    echo OK:file:$T
    [ -n "$NTP" ]&&{ grep -q '^\[Time\]' /etc/systemd/timesyncd.conf||echo '[Time]'>>/etc/systemd/timesyncd.conf; sed -i "s/^NTP=.*/NTP=$NTP/" /etc/systemd/timesyncd.conf 2>/dev/null||sed -i "/^\[Time\]/a NTP=$NTP" /etc/systemd/timesyncd.conf; systemctl restart systemd-timesyncd 2>/dev/null; systemctl is-active systemd-timesyncd&>/dev/null&&echo OK:ntp||echo FAIL:ntp; }||echo SKIP:ntp
    netplan apply&&echo OK:apply||{ echo FAIL:apply;exit 1; }
    a=$(ip -4 -o a show dev $IF|awk '{print $4}'|head -1);g=$(ip route show default|awk '{print $3}'|head -1)
    [ "$a"="$IP/$MASK" ]&&echo OK:ip||echo FAIL:ip:$a;[ "$g"="$GW" ]&&echo OK:gw||echo FAIL:gw:$g
    EOF
    

    2. CentOS/Rocky 单网口(ifcfg)

    sudo bash <<'EOF'
    IP='10.10.10.10'
    GW='10.10.10.254'
    DNS='';NTP='';MASK='24';IF='*'
    [ -z "$DNS" ]&&DNS=$GW
    P(){ ip -br -4 a|awk '$1!~"^(lo|docker|veth|kube|cni|br-|flannel)"&&$2=="UP"&&$3~/\//{print($1~/^bond/?100:$1~/^team/?100:$1~/^eth/?90:$1~/^ens/?80:10),$1}'|sort -rn|head -1|awk '{print $2}'; }
    [ "$IF"="*" ]&&IF=$(P);[ -n "$IF" ]||{ echo FAIL:iface;exit 1; }
    echo "CFG: $IF $IP/$MASK gw=$GW dns=$DNS${NTP:+ ntp=$NTP}"
    D=/root/nb-$(date +%Y%m%d%H%M%S);mkdir -p $D;cp -a /etc/sysconfig/network-scripts/ifcfg-* $D/ 2>/dev/null&&echo OK:backup:$D||echo FAIL:backup
    cat>/etc/sysconfig/network-scripts/ifcfg-$IF<<C
    DEVICE=$IF
    ONBOOT=yes
    BOOTPROTO=none
    IPADDR=$IP
    PREFIX=$MASK
    GATEWAY=$GW
    DNS1=$DNS
    C
    echo OK:file:ifcfg-$IF
    [ -n "$NTP" ]&&{ mkdir -p /etc/chrony.d;echo "server $NTP iburst">/etc/chrony.d/99-custom.conf;systemctl restart chronyd 2>/dev/null; pgrep chronyd&>/dev/null&&echo OK:ntp||echo FAIL:ntp; }||echo SKIP:ntp
    if nmcli dev status&>/dev/null;then nmcli con reload 2>/dev/null;nmcli dev reapply $IF&&echo OK:apply||echo FAIL:apply;else systemctl restart network 2>/dev/null&&echo OK:apply||{ ip link set $IF up;ip addr flush dev $IF 2>/dev/null;ip addr add $IP/$MASK dev $IF;ip route replace default via $GW dev $IF; echo OK:apply:ip; };fi
    a=$(ip -4 -o a show dev $IF|awk '{print $4}'|head -1);g=$(ip route show default|awk '{print $3}'|head -1)
    [ "$a"="$IP/$MASK" ]&&echo OK:ip||echo FAIL:ip:$a;[ "$g"="$GW" ]&&echo OK:gw||echo FAIL:gw:$g
    EOF
    

    3. Ubuntu/Debian 双网口聚合 bond0(netplan)

    sudo bash <<'EOF'
    IP='10.10.10.10'
    GW='10.10.10.254'
    DNS='';NTP='';MASK='24';S1='*';S2='*';BO=bond0
    [ -z "$DNS" ]&&DNS=$GW
    L(){ ip -br link|awk '$1!~"^(lo|docker|veth|kube|cni|br-|bond|team)"&&$2=="UP"{print($1~/^eth/?90:$1~/^ens/?80:50),$1}'|sort -rn|awk '{print $2}'; }
    if [ "$S1"="*" ];then
     [ -r /sys/class/net/$BO/bonding/slaves ]&&read S1 S2 _ </sys/class/net/$BO/bonding/slaves
     [ -z "$S1" ]&&{ A=($(L));S1=${A[0]};S2=${A[1]}; }
     [ -n "$S1" ]&&[ -n "$S2" ]||{ echo FAIL:iface;exit 1; }
    fi
    echo "CFG: $BO($S1+$S2) $IP/$MASK gw=$GW dns=$DNS${NTP:+ ntp=$NTP}"
    D=/root/nb-$(date +%Y%m%d%H%M%S);mkdir -p $D;cp -a /etc/netplan $D/ 2>/dev/null&&echo OK:backup:$D||echo FAIL:backup
    shopt -s nullglob;Y=(/etc/netplan/*.yaml);T=$(printf '%s\n' "${Y[@]}"|sort|head -1)
    for f in "${Y[@]}";do grep -qE "bonds:| $BO:" "$f"&&T=$f;done
    R=$(grep -h renderer: $D/netplan/*.yaml 2>/dev/null|awk '{print $2}'|head -1);R=${R:-networkd}
    for f in /etc/netplan/*.yaml;do [ "$f"!="$T" ]&&rm -f "$f";done;shopt -u nullglob
    cat>$T<<E
    network:
      version: 2
      renderer: $R
      ethernets:
        $S1: {dhcp4: no}
        $S2: {dhcp4: no}
      bonds:
        $BO:
          dhcp4: no
          interfaces: [$S1, $S2]
          parameters: {mode: active-backup}
          addresses: [$IP/$MASK]
          routes: [{to: default, via: $GW}]
          nameservers: {addresses: [$DNS]}
    E
    echo OK:file:$T
    [ -n "$NTP" ]&&{ grep -q '^\[Time\]' /etc/systemd/timesyncd.conf||echo '[Time]'>>/etc/systemd/timesyncd.conf; sed -i "s/^NTP=.*/NTP=$NTP/" /etc/systemd/timesyncd.conf 2>/dev/null||sed -i "/^\[Time\]/a NTP=$NTP" /etc/systemd/timesyncd.conf; systemctl restart systemd-timesyncd 2>/dev/null; systemctl is-active systemd-timesyncd&>/dev/null&&echo OK:ntp||echo FAIL:ntp; }||echo SKIP:ntp
    netplan apply&&echo OK:apply||{ echo FAIL:apply;exit 1; }
    a=$(ip -4 -o a show dev $BO|awk '{print $4}'|head -1);g=$(ip route show default|awk '{print $3}'|head -1)
    [ "$a"="$IP/$MASK" ]&&echo OK:ip||echo FAIL:ip:$a;[ "$g"="$GW" ]&&echo OK:gw||echo FAIL:gw:$g
    EOF
    

    4. CentOS/Rocky 双网口聚合 team0(ifcfg)

    前置:yum install -y teamd NetworkManager-team

    sudo bash <<'EOF'
    IP='10.10.10.10'
    GW='10.10.10.254'
    DNS='';NTP='';MASK='24';S1='*';S2='*';TM=team0
    [ -z "$DNS" ]&&DNS=$GW
    L(){ ip -br link|awk '$1!~"^(lo|docker|veth|kube|cni|br-|bond|team)"&&$2=="UP"{print($1~/^eth/?90:$1~/^ens/?80:50),$1}'|sort -rn|awk '{print $2}'; }
    if [ "$S1"="*" ];then
     [ -d /sys/class/net/$TM ]&&{ A=($(ls /sys/class/net/$TM/lower_* 2>/dev/null|xargs -n1 basename|sed 's/^lower_//')); S1=${A[0]};S2=${A[1]}; }
     [ -z "$S1" ]&&{ A=($(L));S1=${A[0]};S2=${A[1]}; }
     [ -n "$S1" ]&&[ -n "$S2" ]||{ echo FAIL:iface;exit 1; }
    fi
    echo "CFG: $TM($S1+$S2) $IP/$MASK gw=$GW dns=$DNS${NTP:+ ntp=$NTP}"
    D=/root/nb-$(date +%Y%m%d%H%M%S);mkdir -p $D;cp -a /etc/sysconfig/network-scripts/ifcfg-* $D/ 2>/dev/null&&echo OK:backup:$D||echo FAIL:backup
    cat>/etc/sysconfig/network-scripts/ifcfg-$TM<<C
    DEVICE=$TM
    DEVICETYPE=Team
    TEAM_CONFIG={"runner": {"name": "activebackup"}}
    BOOTPROTO=none
    ONBOOT=yes
    IPADDR=$IP
    PREFIX=$MASK
    GATEWAY=$GW
    DNS1=$DNS
    C
    for S in $S1 $S2;do cat>/etc/sysconfig/network-scripts/ifcfg-$S<<C
    DEVICE=$S
    DEVICETYPE=TeamPort
    TEAM_MASTER=$TM
    BOOTPROTO=none
    ONBOOT=yes
    C
    done
    echo OK:file:ifcfg-$TM,$S1,$S2
    [ -n "$NTP" ]&&{ mkdir -p /etc/chrony.d;echo "server $NTP iburst">/etc/chrony.d/99-custom.conf;systemctl restart chronyd 2>/dev/null; pgrep chronyd&>/dev/null&&echo OK:ntp||echo FAIL:ntp; }||echo SKIP:ntp
    if nmcli dev status&>/dev/null;then nmcli con reload 2>/dev/null;nmcli dev reapply $TM&&echo OK:apply||echo FAIL:apply;else systemctl restart network&&echo OK:apply||echo FAIL:apply;fi
    a=$(ip -4 -o a show dev $TM|awk '{print $4}'|head -1);g=$(ip route show default|awk '{print $3}'|head -1)
    [ "$a"="$IP/$MASK" ]&&echo OK:ip||echo FAIL:ip:$a;[ "$g"="$GW" ]&&echo OK:gw||echo FAIL:gw:$g
    EOF
    

    主机与网络信息查看命令

    一键输出主机 OS、物理机/虚拟机、网卡类型,以及业务网卡的 IP、掩码、网关、DNS、NTP、MAC 等网络配置,用于现场排查或填写网络工单。自动识别业务网卡(优先 bond > eth > ens/enp),排除 Docker、K8s、Flannel 等内部网卡;可通过 IFACE 指定接口。DNS 依次尝试 NetworkManager、systemd-resolved、resolv.conf、ifcfg;NTP 识别 chronyd / ntpd / systemd-timesyncd,未显式配置时标注系统默认值。

    sudo -E bash <<'EOF'
    set -eo pipefail
    IFACE='*'
    
    _os(){ . /etc/os-release 2>/dev/null && echo "${PRETTY_NAME:-$NAME} | kernel $(uname -r)" || uname -sr; }
    _host(){
      v=$(systemd-detect-virt 2>/dev/null | head -1 | tr -d ' \t\r\n' || true)
      s=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo -)
      p=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo -)
      if [ "$v" = none ] || [ -z "$v" ]; then
        case "$s $p" in *[Vv]irtual*|*VMware*|*KVM*|*QEMU*|*Hyper-V*|*Xen*|*OpenStack*) echo "VM | $s $p";; *) echo "physical | $s $p";; esac
      else echo "VM ($v) | $s $p"; fi
    }
    _nic(){
      if=$1; [ -r "/sys/class/net/$if/bonding/slaves" ] && { echo "$if | bonded NIC (bonding: $(tr ' ' '+' < /sys/class/net/$if/bonding/slaves))"; return; }
      d=$(ethtool -i "$if" 2>/dev/null | awk '/^driver:/{print $2;exit}')
      case "$d" in virtio_net|vmxnet3|veth|tap|tun|dummy) echo "$if | virtual NIC ($d)";; ""|unknown) echo "$if | unknown";; *) echo "$if | physical NIC ($d)";; esac
    }
    _pick(){ ip -br -4 a 2>/dev/null | awk '
      $1!~"^(lo|docker|cni|flannel|nodelocaldns|veth|kube|calico|tun|tap|virbr|br-|podman)"&&$2=="UP"&&$3~/\//{
        ip=$3;sub(/\/.*/,"",ip); if(ip~/^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ($1~/^bond/?100:$1~/^eth/?90:$1~/^(ens|enp)/?80:10),$1,$3
      }' | sort -rn | head -1 | awk '{print $2,$3}'; }
    _dns(){
      if=$1; ns=""; dom=""; src=""
      if command -v nmcli &>/dev/null; then
        ns=$(nmcli -t -f IP4.DNS dev show "$if" 2>/dev/null | awk -F: '$2~/^[0-9]/{print $2}' | sort -u | paste -sd ', ' -)
        dom=$(nmcli -t -f IP4.DOMAIN dev show "$if" 2>/dev/null | awk -F: '$2!=""{print $2}' | paste -sd ', ' -)
        [ -n "$ns" ] && src=NetworkManager
      fi
      if [ -z "$ns" ] && command -v resolvectl &>/dev/null; then
        ns=$(resolvectl dns "$if" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort -u | paste -sd ', ' -)
        [ -z "$dom" ] && dom=$(resolvectl domain "$if" 2>/dev/null | awk '{$1="";gsub(/^ +/,"");if($0&&$0!~"~.")print}' | paste -sd ', ' -)
        [ -n "$ns" ] && src=systemd-resolved
      fi
      if [ -z "$ns" ]; then
        ns=$(awk '/^nameserver /{print $2}' /etc/resolv.conf 2>/dev/null | grep -Ev '^127\.|^::1' | paste -sd ', ' -)
        [ -n "$ns" ] && src=/etc/resolv.conf
      fi
      if [ -z "$ns" ] && [ -f "/etc/sysconfig/network-scripts/ifcfg-$if" ]; then
        ns=$(grep -E '^DNS[0-9]+=' "/etc/sysconfig/network-scripts/ifcfg-$if" | cut -d= -f2 | paste -sd ', ' -)
        [ -z "$dom" ] && dom=$(grep '^DOMAIN=' "/etc/sysconfig/network-scripts/ifcfg-$if" | cut -d= -f2)
        [ -n "$ns" ] && src=ifcfg
      fi
      [ -z "$dom" ] && dom=$(awk '/^search /{$1="";print}' /etc/resolv.conf 2>/dev/null | tr ' ' ', ')
      echo "${ns:-—}|${dom:-—}|${src:-—}"
    }
    _ntp(){
      svc=""; cfg=""; srv=""; cur=""; st=""; note=""
      st=$(timedatectl show -p NTPSynchronized --value 2>/dev/null)
      [ -z "$st" ] && st=$(timedatectl status 2>/dev/null | awk -F': *' '/System clock synchronized/{v=$2} /NTP synchronized/{v=$2} END{print v}')
      st=${st:-unknown}
      _csrv(){ grep -Erh '^(server|pool)[[:space:]]' "$@" 2>/dev/null | grep -v '^#' | awk '{print $2}' | grep -E '^[a-zA-Z0-9._-]+\.' | sort -u | awk 'NR>1{printf ", "}{printf $0}'; }
      _crun(){ systemctl is-active --quiet chronyd 2>/dev/null || pidof chronyd &>/dev/null; }
      if [ -f /etc/chrony.conf ] || [ -f /etc/chrony/chrony.conf ] || command -v chronyc &>/dev/null; then
        for f in /etc/chrony.conf /etc/chrony/chrony.conf; do [ -f "$f" ] && cfg=$f && break; done
        srv=$(_csrv /etc/chrony.conf /etc/chrony/chrony.conf /etc/chrony.d/)
        if _crun; then
          svc="chronyd (running)"
          cur=$(chronyc -n tracking 2>/dev/null | grep -oE '\([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\)' | head -1 | tr -d '()')
          [ -z "$cur" ] && cur=$(chronyc -n sources 2>/dev/null | awk '/^\^\* /{print $2;exit}')
          [ "$st" = "unknown" ] && st=$(chronyc tracking 2>/dev/null | awk '/Leap status/{print ($2~/Normal/?"yes":"no");exit}')
        else
          svc="chronyd (stopped)"
          note="chronyd not running; run systemctl start chronyd"
          [ -z "$srv" ] && { srv="(not configured)"; note="no server/pool in chrony.conf and service stopped"; }
        fi
      elif [ -f /etc/ntp.conf ] || command -v ntpq &>/dev/null; then
        svc="ntpd"; cfg=/etc/ntp.conf; srv=$(_csrv /etc/ntp.conf)
        if systemctl is-active --quiet ntpd 2>/dev/null || pidof ntpd &>/dev/null; then
          svc="ntpd (running)"; cur=$(ntpq -pn 2>/dev/null | awk '/^\*/{print $2;exit}')
          [ "$st" = "unknown" ] && st=$([ -n "$cur" ] && echo yes || echo no)
        else
          svc="ntpd (stopped)"; note="ntpd not running"; [ -z "$srv" ] && srv="(not configured)"
        fi
      elif systemctl is-active --quiet systemd-timesyncd 2>/dev/null || [ -f /etc/systemd/timesyncd.conf ]; then
        cfg=/etc/systemd/timesyncd.conf
        systemctl is-active --quiet systemd-timesyncd 2>/dev/null && svc="systemd-timesyncd (running)" || svc="systemd-timesyncd (stopped)"
        n=$(awk -F= '/^NTP=/{gsub(/ /,"",$2);if($2)print $2}' "$cfg" 2>/dev/null)
        fb=$(awk -F= '/^FallbackNTP=/{gsub(/ /,"",$2);if($2)print $2}' "$cfg" 2>/dev/null)
        if [ -n "$n" ]; then srv=$(echo "$n" | tr ' ' ', ')
        elif [ -n "$fb" ]; then srv=$(echo "$fb" | tr ' ' ', '); note="NTP= unset, using FallbackNTP"
        else srv="ntp.ubuntu.com, 0~3.ubuntu.pool.ntp.org"; note="NTP=/FallbackNTP= unset, using Ubuntu defaults"; fi
        cur=$(timedatectl show-timesync -p ServerName --value 2>/dev/null)
      else
        svc="(not configured)"; note="no chrony/ntp/timesyncd config or service found"
      fi
      echo "${svc}|${cfg:-—}|${srv:-—}|${cur:-—}|${st}|${note:-—}"
    }
    
    if [ "$IFACE" = "*" ]; then read IF CIDR _ < <(_pick); else
      CIDR=$(ip -br -4 a show dev "$IFACE" 2>/dev/null | awk '$2=="UP"{print $3;exit}')
      [ -n "$CIDR" ] || { echo "[ERROR] interface $IFACE not found or not UP"; exit 1; }; IF=$IFACE
    fi
    [ -n "${IF:-}" ] || { echo "[ERROR] no private business interface found"; exit 1; }
    ADDR=${CIDR%%/*}; PFX=${CIDR#*/}
    MASK=$(awk -v p="$PFX" 'BEGIN{for(i=0;i<4;i++){n=p-i*8;if(n>=8)m[i]=255;else if(n<=0)m[i]=0;else m[i]=256-2^(8-n)}printf "%d.%d.%d.%d",m[0],m[1],m[2],m[3]}')
    GW=$(ip -4 route show default dev "$IF" 2>/dev/null | awk '{print $3;exit}'); GW=${GW:-$(ip -4 route show default 2>/dev/null | awk '{print $3;exit}')}; GW=${GW:-—}
    MAC=$(cat /sys/class/net/$IF/address 2>/dev/null || echo —); MTU=$(cat /sys/class/net/$IF/mtu 2>/dev/null || echo —)
    IFS='|' read -r DNS DOM DNS_SRC <<< "$(_dns "$IF")"
    IFS='|' read -r NTP_SVC NTP_CFG NTP_SRV NTP_CUR NTP_SYNC NTP_NOTE <<< "$(_ntp)"
    
    echo "=== Host Info ==="
    echo "OS: $(_os)"
    echo "Host type: $(_host)"
    echo "Hostname:   $(hostname -f 2>/dev/null || hostname)"
    echo
    echo "=== Network Info ==="
    echo "NIC type: $(_nic "$IF")"
    echo "MAC/MTU:  $MAC | $MTU"
    echo "IPv4:     $ADDR"
    echo "Netmask:     $MASK (/$PFX)"
    echo "Gateway:     $GW"
    echo "DNS:      $DNS"
    echo "Search domain:   $DOM"
    echo "DNS source:  $DNS_SRC"
    echo
    echo "=== NTP ==="
    echo "NTP service:  $NTP_SVC"
    echo "Config file: $NTP_CFG"
    echo "NTP servers: $NTP_SRV"
    echo "Current source: ${NTP_CUR:-—}"
    echo "Synchronized:   $NTP_SYNC"
    [ "$NTP_NOTE" != "—" ] && [ -n "$NTP_NOTE" ] && echo "Note:     $NTP_NOTE"
    echo
    echo "=== Default Route ==="
    ip -4 route show default 2>/dev/null | sed 's/^/  /' || echo "  (none)"
    echo
    echo "=== Local IPv4 ==="
    ip -br -4 a 2>/dev/null | awk '$1!~"^lo$"{print "  "$0}'
    EOF
    

    ping 网段可用 IP 探测命令

    在内网服务器上快速探测本机所在私网 /24 网段中哪些 IP 可能可用,用于申请或分配 IP 前的初步筛选。

    • 自动识别业务网卡(优先 bond > eth > ens/enp),排除 Docker、K8s、Flannel 等内部网卡
    • 仅探测与本机同网段地址,避免跨网段误扫
    • 有 ping 响应 → 已占用;无响应 → 可能可用(目标可能禁 ping,需二次确认)
    • 输出主机 OS、物理机/虚拟机、网卡类型等简要信息
    • 连续 IP 自动合并显示(如 10.129.243.31-40
    sudo -E bash <<'EOF'
    set -euo pipefail
    PATTERN='*'
    IFACE='*'
    TIMEOUT=10
    W=${PING_WAIT:-1}; P=${PRL:-30}
    
    _os(){ [ -r /etc/os-release ] && . /etc/os-release && echo "${PRETTY_NAME:-$NAME} | kernel $(uname -r)" || echo "$(uname -s) $(uname -r)"; }
    
    _host(){
      local v s p; v=$(systemd-detect-virt 2>/dev/null | head -1 | tr -d ' \t\r\n' || true)
      s=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null | tr -d '\r\n' || echo -)
      p=$(cat /sys/class/dmi/id/product_name 2>/dev/null | tr -d '\r\n' || echo -)
      if [ "$v" = none ] || [ -z "$v" ]; then
        case "$s $p" in
          *[Vv]irtual*|*VMware*|*KVM*|*QEMU*|*Hyper-V*|*Xen*|*Bochs*|*VirtualBox*|*OpenStack*)
            echo "VM | $s $p"; return ;;
        esac
        echo "physical | $s $p"; return
      fi
      echo "VM ($v) | $s $p"
    }
    
    _nic(){
      local if=$1 d sl
      if [ -r "/sys/class/net/$if/bonding/slaves" ]; then
        sl=$(tr ' ' '+' < "/sys/class/net/$if/bonding/slaves" | sed 's/+$//')
        echo "$if | bonded NIC (bonding${sl:+: $sl})"; return
      fi
      d=$(ethtool -i "$if" 2>/dev/null | awk '/^driver:/{print $2;exit}')
      case "$d" in
        virtio_net|vmxnet3|veth|tap|tun|dummy|ifb) echo "$if | virtual NIC ($d)";;
        ""|unknown) [ -d "/sys/class/net/$if/bridge" ] && echo "$if | virtual NIC (bridge)" || echo "$if | unknown";;
        *) echo "$if | physical NIC ($d)";;
      esac
    }
    
    _collapse(){ awk -F. '
      function sr(a,b,pa,pb){split(a,pa,".");split(b,pb,".")
        if(pa[1]==pb[1]&&pa[2]==pb[2]&&pa[3]==pb[3])return a"-"pb[4]; return a"-"b}
      {n++;o[n]=$4+0;ip[n]=$0} END{
        if(!n){print "(none)";exit} s=1
        for(i=2;i<=n;i++) if(o[i]!=o[i-1]+1){if(s==i-1)print ip[s];else print sr(ip[s],ip[i-1]); s=i}
        if(s==n)print ip[s]; else print sr(ip[s],ip[n])
      }'; }
    
    _pick_if(){ ip -br -4 a 2>/dev/null | awk '
      $1!~"^(lo|docker|cni|flannel|nodelocaldns|veth|kube|calico|tun|tap|virbr|br-|podman)" &&
      $2=="UP" && $3~/\/24$/ {
        ip=$3; sub(/\/.*/,"",ip)
        if(ip~/^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\./)
          print ($1~/^bond/?100:$1~/^eth/?90:$1~/^(ens|enp)/?80:10),$1,ip
      }' | sort -rn | head -1 | awk '{print $2,$3}'; }
    
    if [ "$IFACE" = "*" ]; then IF=$(_pick_if); else
      IP=$(ip -br -4 a show dev "$IFACE" 2>/dev/null | awk '$2=="UP"{print $3;exit}')
      [ -n "$IP" ] || { echo "[ERROR] interface $IFACE not found or not UP"; exit 1; }
      IF="$IFACE ${IP%%/*}"
    fi
    [ -n "$IF" ] || { echo "[ERROR] no private /24 interface found"; exit 1; }
    read IF IP _ <<< "$IF"
    IFS=. read -r a b c _ <<< "$IP"; SUB="$a.$b.$c"
    
    case "$PATTERN" in
      "*"|".*")  PAT="${SUB}.*" ;;
      *.*.*.*)   PAT="$PATTERN" ;;
      *)         PAT="${SUB}.${PATTERN}" ;;
    esac
    
    IFS=. read -r p1 p2 p3 p4 <<< "$PAT"
    [ "$p1.$p2.$p3" = "$SUB" ] || { echo "[ERROR] PATTERN=${PAT} not in same subnet as ${IF}(${IP}/24)"; exit 1; }
    
    ips=()
    if [ "$p4" = "*" ]; then for i in $(seq 1 254); do ips+=("$SUB.$i"); done
    elif case "$p4" in *'*') true;; *) false;; esac; then
      x="${p4%\*}"; s=$((x*10)); e=$((s+9)); [ $s -lt 1 ] && s=1; [ $e -gt 254 ] && e=254
      for i in $(seq $s $e); do ips+=("$SUB.$i"); done
    elif [[ "$p4" =~ ^[0-9]{1,3}$ ]]; then ips+=("$SUB.$p4")
    else echo "[ERROR] invalid 4th octet: $p4"; exit 1; fi
    
    f=$(mktemp); trap 'rm -f "$f"' EXIT
    par=$P; [ ${#ips[@]} -lt $par ] && par=${#ips[@]}; [ $par -lt 1 ] && par=1
    
    echo "=== Host Info ==="
    echo "OS: $(_os)"
    echo "Host type: $(_host)"
    echo "NIC type: $(_nic "$IF")"
    echo
    echo "=== Ping Scan ==="
    echo "Iface: $IF ($IP/24) | Pattern: $PAT | Count: ${#ips[@]} | Timeout: ${TIMEOUT}s"
    echo
    
    to=0
    timeout "$TIMEOUT" bash -c '
      printf "%s\n" '"$(printf "'%s' " "${ips[@]}")"' | xargs -P '"$par"' -I{} bash -c '\''
        ip="$1"
        ( ping -c1 -W'"$W"' -q "$ip" || sudo ping -c1 -W'"$W"' -q "$ip" ) &>/dev/null \
          && echo "u $ip" || echo "f $ip"
      '\'' _ {}
    ' > "$f" || to=1
    
    sort -t. -k1,1n -k2,2n -k3,3n -k4,4n "$f" -o "$f"
    
    u=$(grep -c '^u ' "$f" 2>/dev/null || true); v=$(grep -c '^f ' "$f" 2>/dev/null || true)
    u=${u:-0}; v=${v:-0}; done=$((u+v)); miss=$((${#ips[@]}-done))
    
    echo "=== In Use ($u) ==="
    grep '^u ' "$f" | awk '{print $2}' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | _collapse
    echo
    echo "=== Possibly Free ($v) ==="
    grep '^f ' "$f" | awk '{print $2}' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | _collapse
    echo
    echo "=== Summary ==="
    echo "In use: $u | Possibly free: $v | Done: $done/${#ips[@]} | Pending: $miss"
    [ $to -eq 1 ] && echo "Note: ${TIMEOUT}s timeout; narrow PATTERN (e.g. 1*) or increase TIMEOUT"
    if [ "$v" -gt 0 ]; then
      echo "All candidates:"
      grep '^f ' "$f" | awk '{print $2}' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | _collapse | sed 's/^/  → /'
    fi
    EOF
    

    MP3