0. 概述
在平时配置系统的时候,总是可以遇到类似于获取网卡地址之类的需求,用 shell 略显复杂,如果可以安装各种第三方包,那么用 python 还是很舒服的,但是,如果不额外安装的话,如何用标准库实现呢,本文尝试解决一番。
1. shell 操作
在平时日常使用中,我们一般情况下都是用 shell 来查看(当然有同学是用 GUI 啦)的,例如 ifconfig
以及更新的 ip
命令都是常见的选择,例如我常用的 ip
命令:
[[email protected]]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:de:11:11 brd ff:ff:ff:ff:ff:ff
inet 192.168.111.111/20 brd 192.168.31.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fede:1f76/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 52:54:00:c9:0c:07 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:15:49:a8:8d brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:15ff:fe49:a88d/64 scope link
valid_lft forever preferred_lft forever
这个信息是很丰富的,足够我们获取很多信息,基本上可以应对日常的需要,例如:
- 网卡名字
- mac 地址
- ip
- mask
- 网关
如果不额外安装库,shell 也不精通的情况下,直接使用 python 执行这条命令,并且解析也是一种不错的选择。
2. ioctl 获取
我肯定对调用 shell 是不满意的,因为我个人更喜欢直接调用标准库,所以一番搜索之后,我发现原来还可以用 ioctl
来获取 socket 的信息:fcntl.ioctl
ioctl 是 APUE 中结果过的一个 io 的高级操作,可以完成很多事情,对于与不同的 io 类型,可以完成的功能也是不同的,对应到 socket 中,可以做到的事情有:
选项 | 功能 |
---|---|
SIOCGIFCONF | 获取所有接口的清单 |
SIOCSIFADDR | 设置接口地址 |
SIOCGIFADDR | 获取接口地址 |
SIOCSIFFLAGS | 设置接口标志 |
SIOCGIFFLAGS | 获取接口标志 |
SIOCSIFDSTADDR | 设置点到点地址 |
SIOCGIFDSTADDR | 获取点到点地址 |
SIOCGIFBRDADDR | 获取广播地址 |
SIOCSIFBRDADDR | 设置广播地址 |
SIOCGIFNETMASK | 获取子网掩码 |
SIOCSIFNETMASK | 设置子网掩码 |
SIOCGIFMETRIC | 获取接口的测度 |
SIOCSIFMETRIC | 设置接口的测度 |
SIOCGIFMTU | 获取接口MTU |
SIOCGXXX | 根据 os 不同支持也不同 |
所以这里很简单,可以通过 SIOCGIFCONF
选项获取到所有的接口以及他们的信息,调用的代码如下:
[[email protected]]# cat get_inet.py
... ...
outbytes = struct.unpack('iL', fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack('iL', bytes, names.buffer_info()[0])
))[0]
... ...
然后再一一解析输出的 interface 信息:
[[email protected]]# cat get_inet.py
... ...
for i in range(0, outbytes, 40):
name = (namestr[i:i + 16]).decode().split('\0', 1)[0]
ip = format_ip(namestr[i + 20:i + 24])
rst[name] = ip
... ...
完整的代码见: get_interface.py
3. Golang 获取接口地址
如果你使用 Go 语言的话,事情就变得更加简单了,直接通过标准库就可以实现了:
[[email protected]]# cat get_interface.go
... ...
ifaces, err := net.Interfaces()
if err != nil {
panic(err)
}
... ...
完整的代码可以参见:get_interface.go