在 Linux 的 python 中检测到连接到 Raspberry Pi 的无线设备

Detect wireless devices connected to Raspberry Pi in python on Linux

在我的 python 代码中,我需要获取连接到 Raspberry Pi

的 "physical" 个 WiFi 网络设备的列表

我一直在通过调用:

raw_output = check_output('iw dev', shell=True)

然后从raw_output

中提取我需要的所有数据

它工作正常,但在 iw help 中它说 Do NOT screenscrape this tool, we don't consider its output stable. 以我的方式获取这些数据真的不安全吗?如果是,正确的做法是什么?

"Do NOT screenscrape this tool, we don't consider its output stable" 的意思是,随着 iw 的新版本发布,输出格式可能会发生变化。因此 iw 的开发人员警告您,如果您根据其输出的解析编写软件,它可能会在 iw.

的未来版本中中断

以古老的ifconfig命令为例。多年来,它的输出格式一直是这样的:

eth0      Link encap:Ethernet  HWaddr 00:80:C8:F8:4A:51
          inet addr:192.168.99.35  Bcast:192.168.99.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:190312 errors:0 dropped:0 overruns:0 frame:0
          TX packets:86955 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:30701229 (29.2 Mb)  TX bytes:7878951 (7.5 Mb)
          Interrupt:9 Base address:0x5000

虽然它被认为是稳定的(甚至被一些人弃用和维护),但它在几年前发生了变化,现在看起来像这样:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.67  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::8e89:a5ff:fe57:103c  prefixlen 64  scopeid 0x20<link>
        ether 8c:89:a5:57:10:3c  txqueuelen 1000  (Ethernet)
        RX packets 2219946  bytes 3178868967 (2.9 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1241676  bytes 102998523 (98.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

...假设我做了一些软件,通​​过搜索 "HWaddr" 之后的字符串来查看 MAC 地址。现在它会被破坏,因为它应该寻找 "ether" 之后的字符串。

但是只要您不更新 iw,或者对您的工作进行定期测试,您应该不会遇到任何问题。

无论如何总是解析第三方工具的输出本质上有点脆弱,你只需要意识到这一点。例如,输出可能取决于用户的 LOCALE 设置。现实生活中的例子,我用 ifconfig 的输出做的一些脚本在某些用户环境中失败了。根本原因:这是法语语言环境中的输出:

eth0      Lien encap:Ethernet  HWaddr 00:FF:F2:58:32:A1  
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          Packets reçus:0 erreurs:0 :0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          Octets reçus:0 (0.0 b) Octets transmis:0 (0.0 b)
          Interruption:23 Adresse de base:0x2000

注意法语 "Packets reçus"、"erreurs" 和 "Octets reçus" 而不是 "RX packets"、"errors" 和 "RX bytes"。

编辑:

所以:

Is it really unsafe to get this data the way I did it?

不是真的。您只需要记住,您的软件依赖于某些第三方软件的输出字符串,这些软件在某种程度上不受您的控制,并且将来可能会发生变化。那将是您的定期测试和维护工作,没什么可悲的,这就是软件生命。

If yes, what is the correct way to do this?

同样,"no",但如果您想对此做到万无一失:不要依赖第三方软件的文本输出。这通常需要编写 您自己的 代码来替换这些工具,这可能是一项艰巨的任务。如果这样做,您可以使用一些第三方库,好吧,库 API 也会随着时间的推移而变化...:-)

编辑 2:

在你的情况下,不依赖于 iw 的输出(即编写你自己的 "mini iw"),并考虑到你想在 Python 中编码:

在低级别 iw,用 C 编写,使用 libnl(也在 C 中)与内核通信以在网络接口上获得 information/perform 操作。

https://www.infradead.org/~tgr/libnl/

你很幸运:似乎有一个 Python libnl 库的主动维护版本。

https://pypi.python.org/pypi/libnl/0.2.0

所以计划是:

  1. 研究 iw C 源代码以了解 netlink/libnl 它对 get/set 您感兴趣的部分执行的操作。
  2. 在您的代码中使用 Python libnl 来复制它。

(请注意,libnl/netlink 被设计为一种非常通用的 long-term 可扩展机制。它的设计目的确实是为了取代 ad-hoc ioctl。具有这种通用性有一定的复杂性:即使是执行简单的任务也可能需要 complicated/involve 大量编码。)

正如我在上面所写的那样,编写自己的代码来替换工具可能是一项艰巨的任务。 grep 命令的输出只需几分钟的代码,而在这里这可能需要数天或数周的工作。所以你必须在 "quick and easy but not so clean" 和 "self-contained, clean, extensible but expensive" 之间做出选择。这取决于:您是否致力于生产 industrial-grade 客户支持的软件,它是公司内部工具,还是只是一个 week-end 业余爱好软件项目。

我不知道是否适用于覆盆子,但使用@jbm 提到的途径我 运行 通过 netlink 和 ioctl 教程开发了 PyRIC (https://github.com/wraith-wireless/PyRIC),它完全是 Python,没有子进程,iw 输出解析等。对于你所要求的,它就像

一样简单
pyw.interfaces() # get all system interfaces
=> ['lo','eth0','wlan']

pyw.winterfaces() # get all system wireless interfaces
=> ['wlan0']

作为开发人员,我将不胜感激。