bash 解析 iw wlan0 扫描输出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17809912/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:58:14  来源:igfitidea点击:

Parsing iw wlan0 scan output

bashparsingsedawk

提问by Ari Malinen

I wrote wlan manager script to handle open/ad-hoc/wep/wpa2 networks. Now im trying to parse iw wlan0 scan output to get nice scan feature to my script. My goal is to get output like this :

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

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

What i have achived already is output like this :

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

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

I have been trying to study bash/sed/awk but havent found yet a way to achieve what im trying. So what is good way to achieve that?

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

回答by Ari Malinen

Here is my final solution based of Sudo_O answer:

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

 == "BSS" {
    MAC = 
    wifi[MAC]["enc"] = "Open"
}
 == "SSID:" {
    wifi[MAC]["SSID"] = 
}
 == "freq:" {
    wifi[MAC]["freq"] = $NF
}
 == "signal:" {
    wifi[MAC]["sig"] =  " " 
}
 == "WPA:" {
    wifi[MAC]["enc"] = "WPA"
}
 == "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"]
    }
}'

Output:

输出:

$ 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

回答by uml?ute

it's generally bad practice to try parsing complex output of programs intended for humans to read (rather than machines to parse).

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

e.g. the output of iwmight change depending on the language settings of the system and/or the version of iw, leaving you with a "manager" that only works on your development machine.

例如, 的输出iw可能会根据系统的语言设置和/或 的版本而改变iw,让您拥有一个只能在您的开发机器上运行的“管理器”。

instead you might use the same interface that iwuses to get it's information: the library backend libnl

相反,您可以使用iw用于获取其信息的相同接口:库后端libnl

you might also want to have a look at the wireless-tools(iwconfig, iwlist,...) that use the libiwlibrary.

您可能还想查看使用libiw库的wireless-tools( iwconfig, iwlist,...) 。

回答by Chris Seymour

Here is an GNU awkscript to get you going that grabs the SSIDs and the channel for each unique BSS:

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

/^BSS / {
    MAC = 
}
/SSID/ {
    wifi[MAC]["SSID"] = 
}
/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"]
    }
}

It should be easy for you to add the new blocks for signal and encryption considering all the studying you have been doing.

考虑到您一直在进行的所有研究,您应该很容易为信号和加密添加新块。

Save the script to file such as wifi.awkand run like:

将脚本保存到文件,例如wifi.awk并运行:

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

The output will be in the formatted requested:

输出将采用所要求的格式:

SSID        channel
wlan-ap     6
test        1

回答by binary koala

Here is a simple Bash function which uses exclusively Bash internals and spawns only one sub-shell:

这是一个简单的 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)"

Output:

输出:

-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

The function can be easily modified to provide additional fields by adding a necessary filter into the while read -r AP while-loop, eg:

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

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

Output:

输出:

-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

回答by puchu

I am using such solution for openwrt:

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

wlan_scan.sh

wlan_scan.sh

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

wlan_scan.awk

wlan_scan.awk

/^BSS/ {
    mac = gensub ( /^BSS[[:space:]]*([0-9a-fA-F:]+).*?$/, "\1", "g", 
-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
); } /^[[:space:]]*signal:/ { signal = gensub ( /^[[:space:]]*signal:[[:space:]]*(\-?[0-9.]+).*?$/, "\1", "g",
 == "SSID:" {
    wifi[MAC]["SSID"] = 
}
); } /^[[:space:]]*SSID:/ { ssid = gensub ( /^[[:space:]]*SSID:[[:space:]]*([^\n]*).*?$/, "\1", "g",
#!/usr/bin/env awk -f

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

result

结果

##代码##

回答by ericshufro

There is a bug in the awk script above.

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

The following code will not work if the SSID has spaces in the name. The received result will be the first token of the SSID name only.

如果 SSID 名称中有空格,以下代码将不起作用。接收到的结果将仅是 SSID 名称的第一个标记。

##代码##

When printing $0, $1, $2:

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

##代码##

One possibly solution is to take a substr of $0 which contains leading spaces, the token "SSID: " and the provided multi-token network name.

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

Any other suggestions?

还有其他建议吗?

回答by cy8g3n

I've taken awk code from Ari Malinenand reworked it a bit, because iw output is not stable and changes, also there are other issues like spaces in SSID. I put it on githubin case if I'll change it in the future.

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

##代码##

Output:

输出:

##代码##

if you wonder what if($2 !~ /Load:/)does, well on some routers there might be "BSS Load:" string.

如果您想知道是什么if($2 !~ /Load:/),那么在某些路由器上可能会有“BSS Load:”字符串。