diff --git a/script/scan-ip.sh b/script/scan-ip.sh index d3f23b6..dfbf5f4 100755 --- a/script/scan-ip.sh +++ b/script/scan-ip.sh @@ -11,6 +11,7 @@ usage() { 扫描局域网主机: 无参数 ICMP 扫描本网段 .1-.254,并尽力显示名称、MAC 和厂商 + 检测到多个 IPv4 网卡时会先让你选择扫描源 --identify 额外通过 mDNS 识别设备(macOS,约增加 3 秒) 10.0.0 扫描指定网段前缀 22 扫描本网段 TCP 22 端口 @@ -92,15 +93,101 @@ if [ -n "$1" ] && [ -z "$PORT_SPEC" ] && [ -z "$PREFIX_ARG" ]; then exit 1 fi -if command -v ip &>/dev/null; then - LOCAL_IP=$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n 1) -elif command -v ifconfig &>/dev/null; then - LOCAL_IP=$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n 1) -else - echo "错误: 找不到 ip 或 ifconfig 命令" && exit 1 -fi +list_ipv4_interfaces() { + if [ "$(uname -s)" = "Darwin" ] && command -v ifconfig &>/dev/null; then + ifconfig | awk ' + /^[^[:space:]][^:]*:/ { + iface=$1 + sub(/:$/, "", iface) + next + } + /^[[:space:]]+inet / { + ip=$2 + if (iface !~ /^lo/ && ip != "127.0.0.1" && ip !~ /^169\.254\./) { + print iface "\t" ip + } + } + ' + elif command -v ip &>/dev/null; then + ip -o -4 addr show up scope global 2>/dev/null \ + | awk '{split($4, address, "/"); print $2 "\t" address[1]}' + elif command -v ifconfig &>/dev/null; then + ifconfig | awk ' + /^[^[:space:]][^:]*:/ { + iface=$1 + sub(/:$/, "", iface) + next + } + /^[[:space:]]+inet / { + ip=$2 + if (iface !~ /^lo/ && ip != "127.0.0.1" && ip !~ /^169\.254\./) { + print iface "\t" ip + } + } + ' + else + return 1 + fi +} -[ -z "$LOCAL_IP" ] && echo "错误: 未能检测到本地 IP" && exit 1 +select_ipv4_interface() { + local iface ip choice entry default_iface default_label index + local candidates=() + + while IFS=$'\t' read -r iface ip; do + [ -n "$iface" ] && [ -n "$ip" ] || continue + candidates+=("$iface|$ip") + done < <(list_ipv4_interfaces) + + if [ ${#candidates[@]} -eq 0 ]; then + echo "错误: 未能检测到可用的 IPv4 网卡" >&2 + return 1 + fi + + default_iface="" + if [ "$(uname -s)" = "Darwin" ] && command -v route &>/dev/null; then + default_iface=$(route -n get default 2>/dev/null \ + | awk '/^[[:space:]]*interface:/{print $2; exit}') + elif command -v ip &>/dev/null; then + default_iface=$(ip route show default 2>/dev/null \ + | awk '{for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}') + fi + + if [ ${#candidates[@]} -gt 1 ]; then + echo "检测到多个可用 IPv4 网卡,请选择扫描源:" >&2 + for index in "${!candidates[@]}"; do + entry=${candidates[$index]} + iface=${entry%%|*} + ip=${entry#*|} + default_label="" + [ "$iface" = "$default_iface" ] && default_label=" (默认路由)" + printf ' %d) %s %s%s\n' "$((index + 1))" "$iface" "$ip" "$default_label" >&2 + done + + while :; do + printf '请选择 [1-%d]: ' "${#candidates[@]}" >&2 + IFS= read -r choice || { + echo >&2 + echo "错误: 未选择扫描源网卡" >&2 + return 1 + } + if [[ "$choice" =~ ^[0-9]+$ ]] \ + && [ "$choice" -ge 1 ] \ + && [ "$choice" -le ${#candidates[@]} ]; then + entry=${candidates[$((choice - 1))]} + break + fi + echo "错误: 请输入 1-${#candidates[@]} 之间的编号" >&2 + done + else + entry=${candidates[0]} + fi + + LOCAL_IFACE=${entry%%|*} + LOCAL_IP=${entry#*|} +} + +select_ipv4_interface || exit 1 if [ -n "$PREFIX_ARG" ]; then PREFIX="$PREFIX_ARG" @@ -127,7 +214,8 @@ if [ -n "$PORT_SPEC" ]; then fi fi -echo "本机 IP: $LOCAL_IP,扫描网段: $PREFIX.1-254" +echo "扫描源网卡: ${LOCAL_IFACE} (${LOCAL_IP})" +echo "本机 IP: ${LOCAL_IP},扫描网段: ${PREFIX}.1-254" if [ ${#PORTS[@]} -gt 0 ]; then echo "扫描端口: ${PORTS[*]} (TCP,超时 ${TIMEOUT}s)" else @@ -398,7 +486,7 @@ if [ ${#PORTS[@]} -eq 0 ]; then for i in $(seq 1 254); do ( ip="$PREFIX.$i" - if ping -c 1 -W 1 -I "$LOCAL_IP" "$ip" >/dev/null 2>&1; then + if ping -c 1 -W 1 -I "$LOCAL_IFACE" "$ip" >/dev/null 2>&1; then printf '%s 在线\n' "$(format_host "$ip")" >"$RESULT_DIR/$i" fi ) & @@ -418,7 +506,7 @@ if ! command -v nc &>/dev/null; then exit 1 fi -NC_OPTS=(-z -w "$TIMEOUT") +NC_OPTS=(-z -w "$TIMEOUT" -s "$LOCAL_IP") if [ "$(uname -s)" = "Darwin" ]; then NC_OPTS+=(-G "$TIMEOUT") fi