解析iw wlan0扫描输出

问题描述 投票:0回答:8

我编写了 wlan 管理器脚本来处理开放/ad-hoc/wep/wpa2 网络。现在我尝试解析 iw wlan0 扫描输出,以便为我的脚本获得良好的扫描功能。我的目标是得到这样的输出:

SSID        channel     signal      encryption
wlan-ap     6           70%         wpa2-psk
test        1           55%         wep

我已经实现的输出是这样的:

$ iw wlan0 scan | grep 'SSID\|freq\|signal\|capability' | tac
SSID: Koti783
signal: -82.00 dBm
capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime (0x0531)
freq: 2437

我一直在尝试研究 bash/sed/awk 但还没有找到一种方法来实现我正在尝试的目标。那么实现这一目标的好方法是什么?

bash parsing sed awk
8个回答
13
投票

这是我基于 Sudo_O 答案的最终解决方案:

$1 == "BSS" {
    MAC = $2
    wifi[MAC]["enc"] = "Open"
}
$1 == "SSID:" {
    wifi[MAC]["SSID"] = $2
}
$1 == "freq:" {
    wifi[MAC]["freq"] = $NF
}
$1 == "signal:" {
    wifi[MAC]["sig"] = $2 " " $3
}
$1 == "WPA:" {
    wifi[MAC]["enc"] = "WPA"
}
$1 == "WEP:" {
    wifi[MAC]["enc"] = "WEP"
}
END {
    printf "%s\t\t%s\t%s\t\t%s\n","SSID","Frequency","Signal","Encryption"

    for (w in wifi) {
        printf "%s\t\t%s\t\t%s\t%s\n",wifi[w]["SSID"],wifi[w]["freq"],wifi[w]["sig"],wifi[w]["enc"]
    }
}'

输出:

$ sudo iw wlan0 scan | awk -f scan.awk
SSID        Frequency   Signal      Encryption
netti       2437        -31.00 dBm  Open
Koti783     2437        -84.00 dBm  WPA
WLAN-AP     2462        -85.00 dBm  WPA

8
投票

尝试解析供人类阅读(而不是机器解析)的程序的复杂输出通常是不好的做法。

例如

iw
的输出可能会根据系统的语言设置和/或
iw
的版本而变化,为您留下一个仅适用于您的开发计算机的“管理器”。

相反,您可以使用

iw
用于获取其信息的相同接口:库后端 libnl

您可能还想看看使用

libiw
库的
wireless-tools
iwconfig
iwlist...)。


5
投票

这里有一个

GNU awk
脚本可以帮助您获取每个唯一 BSS 的 SSID 和通道:

/^BSS / {
    MAC = $2
}
/SSID/ {
    wifi[MAC]["SSID"] = $2
}
/primary channel/ {
    wifi[MAC]["channel"] = $NF
}

# Insert new block here

END {
    printf "%s\t\t%s\n","SSID","channel"

    for (w in wifi) {
        printf "%s\t\t%s\n",wifi[w]["SSID"],wifi[w]["channel"]
    }
}

考虑到您一直在做的所有学习,您应该很容易添加新的信号和加密块。

将脚本保存到文件,例如

wifi.awk
并运行:

$ sudo iw wlan0 scan | awk -f wifi.awk

输出将采用请求的格式:

SSID        channel
wlan-ap     6
test        1

5
投票

这是一个简单的 Bash 函数,它专门使用 Bash 内部结构并仅生成一个子 shell:

#!/bin/bash 
function iwScan() {
   # disable globbing to avoid surprises
   set -o noglob
   # make temporary variables local to our function
   local AP S
   # read stdin of the function into AP variable
   while read -r AP; do
     ## print lines only containing needed fields
     [[ "${AP//'SSID: '*}" == '' ]] && printf '%b' "${AP/'SSID: '}\n"
     [[ "${AP//'signal: '*}" == '' ]] && ( S=( ${AP/'signal: '} ); printf '%b' "${S[0]},";)
   done
   set +o noglob
}

iwScan <<< "$(iw wlan0 scan)"

输出:

-66.00,FRITZ!Box 7312
-56.00,ALICE-WLAN01
-78.00,o2-WLAN93
-78.00,EasyBox-7A2302
-62.00,dlink
-74.00,EasyBox-59DF56
-76.00,BELAYS_Network
-82.00,o2-WLAN20
-82.00,BPPvM

可以通过在 while read -r AP while 循环中添加必要的过滤器来轻松修改该函数以提供其他字段,例如:

[[ "${AP//'last seen: '*}" == '' ]] && ( S=( ${AP/'last seen: '} ); printf '%b' "${S[0]},";)

输出:

-64.00,1000,FRITZ!Box 7312
-54.00,492,ALICE-WLAN01
-76.00,2588,o2-WLAN93
-78.00,652,LN8-Gast
-72.00,2916,WHITE-BOX
-66.00,288,ALICE-WLAN
-78.00,800,EasyBox-59DF56
-80.00,720,EasyBox-7A2302
-84.00,596,ALICE-WLAN08

2
投票

我正在为 openwrt 使用这样的解决方案:

wlan_scan.sh

#!/bin/sh
sudo iw dev wlan0 scan | awk -f wlan_scan.awk | sort

wlan_scan.awk

/^BSS/ {
    mac = gensub ( /^BSS[[:space:]]*([0-9a-fA-F:]+).*?$/, "\\1", "g", $0 );
}
/^[[:space:]]*signal:/ {
    signal = gensub ( /^[[:space:]]*signal:[[:space:]]*(\-?[0-9.]+).*?$/, "\\1", "g", $0 );
}
/^[[:space:]]*SSID:/ {
    ssid = gensub ( /^[[:space:]]*SSID:[[:space:]]*([^\n]*).*?$/, "\\1", "g", $0 );
    printf ( "%s %s %s\n", signal, mac, ssid );
}

结果

-62.00 c8:64:c7:54:d9:05 a
-72.00 70:72:3c:1c:af:17 b
-81.00 78:f5:fd:be:33:cb c

0
投票

上面的 awk 脚本中有一个错误。

如果 SSID 名称中包含空格,则以下代码将不起作用。收到的结果将只是 SSID 名称的第一个令牌。

$1 == "SSID:" {
    wifi[MAC]["SSID"] = $2
}

打印$0、$1、$2时:

$0:     SSID: DIRECT-82-HP OfficeJet 8700
$1: SSID:
$2: DIRECT-82-HP

一种可能的解决方案是采用 $0 的子字符串,其中包含前导空格、令牌“SSID:”和提供的多令牌网络名称。

还有其他建议吗?


0
投票

我从Ari Malinen获取了awk代码并对其进行了一些修改,因为iw输出不稳定并且发生变化,还存在其他问题,例如SSID中的空格。我把它放在 github 上,以防将来我更改它。

#!/usr/bin/env awk -f

$1 ~ /^BSS/ {
    if($2 !~ /Load:/) { #< Escape "BBS Load:" line
        gsub("(\\(.*|:)", "", $2)
        MAC = toupper($2)
        wifi[MAC]["enc"] = "OPEN"
        wifi[MAC]["WPS"] = "no"
        wifi[MAC]["wpa1"] = ""
        wifi[MAC]["wpa2"] = ""
        wifi[MAC]["wep"] = ""
    }
}
$1 == "SSID:" {
    # Workaround spaces in SSID
    FS=":" #< Changing field separator on ":", it should be
           #  forbidded sign for SSID name
    $0=$0
    sub(" ", "", $2) #< remove first whitespace
    wifi[MAC]["SSID"] = $2
    FS=" "
    $0=$0
}
$1 == "capability:" {
    for(i=2; i<=NF; i++) {
        if($i ~ /0x[0-9]{4}/) {
            gsub("(\\(|\\))", "", $i)
            if (and(strtonum($i), 0x10)) 
                wifi[MAC]["wep"] = "WEP"
        }
    }
}
$1 == "WPA:" {
    wifi[MAC]["wpa1"] = "WPA1"
}
$1 == "RSN:" {
    wifi[MAC]["wpa2"] = "WPA2"
}
$1 == "WPS:" {
    wifi[MAC]["WPS"] = "yes"
}
$1 == "DS" {
    wifi[MAC]["Ch"] = $5
}
$1 == "signal:" {
    match($2, /-([0-9]{2})\.00/, m)
    wifi[MAC]["Sig"] = m[1]
}
$1 == "TSF:" {
    gsub("(\\(|d|,)", "", $4)
    match($5, /([0-9]{2}):([0-9]{2}):/, m)
    day = $4
    hour = m[1]
    min = m[2]
    wifi[MAC]["TSF"] = day"d"hour"h"min"m"
}
END {
    for (w in wifi) {
        if (wifi[w]["wep"]) {
            if (wifi[w]["wpa1"] || wifi[w]["wpa2"])
                wifi[w]["enc"] = wifi[w]["wpa1"]wifi[w]["wpa2"]
            else
                wifi[w]["enc"] = "WEP"
        }
        printf "%s:%s:%s:%s:%s:%s:%s\n", w, wifi[w]["SSID"], wifi[w]["enc"], \
               wifi[w]["WPS"], wifi[w]["Ch"], wifi[w]["Sig"], wifi[w]["TSF"]
    }
}

输出:

A5FEF2C499BB:test-ssid2:OPEN:no:9:43:0d00h00m
039EFACA9A8B:test-ssid2:WPA1:no:9:33:0d00h00m
038BF3C1988B:test-ssid2:WPA2:no:9:35:0d00h00m
028EF3C2997B:test-ssid2:WPA1:no:9:35:0d00h03m

如果您想知道

if($2 !~ /Load:/)
的作用,那么在某些路由器上可能会有“BSS Load:”字符串。


0
投票

您可以使用jc(JSON转换)

jc JSON 化了许多 CLI 工具、文件类型和常见字符串的输出,以便于在脚本中进行解析。

$ iw dev wlan0 scan | jc --iw-scan

$ jc iw dev wlan0 scan
© www.soinside.com 2019 - 2024. All rights reserved.