本文介绍了python获取linux系统中各种信息的方法,包括系统类型、cpu信息、内存信息、块设备等,python编程语言工具来检索linux系统各种信息,感兴趣的朋友参考下。
/proc/net/dev文件让这些信息可用。如果你检查了这个文件的内容,你就会注意到头一两行包含了头信息等等,这个文件第一列是网络接口名,第二和第三列显示了接收和发送的字节数信息(例如总发送字节数,包数,错误等等)。
不同的网络设备提取出总发送数据和接收数据。
例子,怎么从/proc/net/dev文件中提取出这些信息。
复制代码 代码示例:
#!/usr/bin/env python
from __future__ import print_function
from collections import namedtuple
def netdevs():
''' RX and TX bytes for each of the network devices '''
with open('/proc/net/dev') as f:
net_dump = f.readlines()
device_data={}
data = namedtuple('data',['rx','tx'])
for line in net_dump[2:]:
line = line.split(':')
if line[0].strip() != 'lo':
device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0),
float(line[1].split()[8])/(1024.0*1024.0))
return device_data
if __name__=='__main__':
netdevs = netdevs()
for dev in netdevs.keys():
print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))
当运行上面的程序,下面这个输出就会显示从你最近重启之后网络设备总接收和发送的数据,单位为兆。
em1: 0.0 MiB 0.0 MiB
wlan0: 2651.40951061 MiB 183.173976898 MiB
可以使用持久的数据存储机制来连接,来写出自己的数据使用监控程序。
六,进程信息
/proc目录包含了所有正运行的进程目录。这些目录的名字和进程的标识符是一样的。
所以,如果遍历/proc目录下那些使用数字作为它们的名字的目录,就会获得所有现在正在运行的进程列表。
以下代码中process_list()函数返回所有现在正在运行的进程的标识符列表。
当执行这个程序后,这个列表的长度就是在系统上运行的总进程数。
例子:
复制代码 代码示例:
#!/usr/bin/env python
"""
List of all process IDs currently active
"""
from __future__ import print_function
import os
def process_list():
pids = []
for subdir in os.listdir('/proc'):
if subdir.isdigit():
pids.append(subdir)
return pids
if __name__=='__main__':
pids = process_list()
print('Total number of running processes:: {0}'.format(len(pids)))
上面的程序当执行后会显示和下面类似的输出:
Total number of running processes:: 229
每个进程目录包含了一些其他文件和目录,如进程命令的调用,它正使用的共享库以及其它的。
七,块设备
下一个程序通过读sysfs虚拟文件系统列出所有块设备。
系统中的块设备可以从/sys/block目录中找到。因此可能会有/sys/block/sda、/sys/block/sdb等这样的目录。
为了获取所有这些设备,使用正则表达式对/sys/block目录进行扫描提取感兴趣的块设备。
复制代码 代码示例:
#!/usr/bin/env python
"""
Read block device data from sysfs
"""
from __future__ import print_function
import glob
import re
import os
# Add any other device pattern to read from
dev_pattern = ['sd.*','mmcblk*']
def size(device):
nr_sectors = open(device+'/size').read().rstrip('n')
sect_size = open(device+'/queue/hw_sector_size').read().rstrip('n')
# The sect_size is in bytes, so we convert it to GiB and then send it back
return (float(nr_sectors)*float(sect_size))/(1024.0*1024.0*1024.0)
def detect_devs():
for device in glob.glob('/sys/block/*'):
for pattern in dev_pattern:
if re.compile(pattern).match(os.path.basename(device)):
print('Device:: {0}, Size:: {1} GiB'.format(device, size(device)))
if __name__=='__main__':
detect_devs()
如果运行该程序,将会看到下述类似的输出:
Device:: /sys/block/sda, Size:: 465.761741638 GiB
Device:: /sys/block/mmcblk0, Size:: 3.70703125 GiB
当运行该程序时,有个SD内存卡插在电脑上,因此会看到程序检测到了它。
也可以扩展该程序识别其它块设备(比如虚拟硬盘)。
八,建立命令行实用工具
linux中命令行使用工具是无所不在的[@Lesus 注:曾有人说过:linux没有了命令行就是个渣。],它允许人么指定命令行参数来定制程序的默认行为。argparse模块就提供了和linux命令行实用工具类似的接口。下面的代码展示了程序如何获得系统上的所有用户以及打印它们的登录shell(使用了pwd标准库模块):
复制代码 代码示例:
#!/usr/bin/env python
"""
Print all the users and their login shells
"""
from __future__ import print_function
import pwd
# Get the users from /etc/passwd
def getusers():
users = pwd.getpwall()
for user in users:
print('{0}:{1}'.format(user.pw_name, user.pw_shell))
if __name__=='__main__':
getusers()
当运行这个程序之后,它会打印系统上所有的用户以及他们登录shell名。
现在,你想要程序的用户能够选择是否想看系统用户(像daemon, apache)。我们扩展前面的代码,第一次使用argparse模块来实现这个特性,如下。
复制代码 代码示例:
#!/usr/bin/env python
"""
Utility to play around with users and passwords on a Linux system
"""
from __future__ import print_function
import pwd
import argparse
import os
def read_login_defs():
uid_min = None
uid_max = None
if os.path.exists('/etc/login.defs'):
with open('/etc/login.defs') as f:
login_data = f.readlines()
for line in login_data:
if line.startswith('UID_MIN'):
uid_min = int(line.split()[1].strip())
if line.startswith('UID_MAX'):
uid_max = int(line.split()[1].strip())
return uid_min, uid_max
# Get the users from /etc/passwd
def getusers(no_system=False):
uid_min, uid_max = read_login_defs()
if uid_min is None:
uid_min = 1000
if uid_max is None:
uid_max = 60000
users = pwd.getpwall()
for user in users:
if no_system:
if user.pw_uid >= uid_min and user.pw_uid <= uid_max:
print('{0}:{1}'.format(user.pw_name, user.pw_shell))
else:
print('{0}:{1}'.format(user.pw_name, user.pw_shell))
if __name__=='__main__':
parser = argparse.ArgumentParser(description='User/Password Utility')
parser.add_argument('--no-system', action='store_true',dest='no_system',
default = False, help='Specify to omit system users')
args = parser.parse_args()
getusers(args.no_system)
使用--help选项执行上面的程序,你会看到友好的帮助信息:可选项以及它们的作用。
复制代码 代码示例:
$ ./getusers.py --help
usage: getusers.py [-h] [--no-system]
User/Password Utility
optional arguments:
-h, --help show this help message and exit
--no-system Specify to omit system users
上面程序使用的一个例子,如下所示:
复制代码 代码示例:
$ ./getusers.py --no-system
gene:/bin/bash
当你传入一个非法的参数,这个程序就会发牢骚(报错)
复制代码 代码示例:
$ ./getusers.py --param
usage: getusers.py [-h] [--no-system]
getusers.py: error: unrecognized arguments: --param
在上面的程序中,我们简单的理解了如何使用argparse模块。parser = argparse.ArgumentParser(description="User/Password Utility")语句创建了一个带说明程序是做什么的可选描述的ArgumentParser对象,然后,我们添加参数。我们想要程序能够识别接下来这条语句 add_argument()。
parser.add_argument('--no-system', action='store_true', dest='no_system', default = False, help='Specify to omit system users')。第一个方法的参数是当系统调用这个程序,程序使用着将要提供这个参数的名称,接下来的参数acton=store_true表明它是一个布尔选择。那就是说,它真或假影响程序的某些行为。dest为可定制化参数,它的值可以提供给程序使用。假如这个值用户不提供,这个值默认false。最后的参数程序显示的帮助信息。最后,参数被解析通过args=parser.parse_args()方法。
一旦解析方法被做,用户选项的值能够被抓取到通过相应的语法参数option_dest,当你配置参数时,option_dest是一个你指定的目的变量。getusers(args.no_system)这条语句使用用户提供参数的值将会回调getusers()方法。
以下展示了如何指定非布尔类型的选项。
该程序是对第6个程序的重写,附加了一个选项用于指定你感兴趣的网络设备。
复制代码 代码示例:
#!/usr/bin/env python
from __future__ import print_function
from collections import namedtuple
import argparse
def netdevs(iface=None):
''' RX and TX bytes for each of the network devices '''
with open('/proc/net/dev') as f:
net_dump = f.readlines()
device_data={}
data = namedtuple('data',['rx','tx'])
for line in net_dump[2:]:
line = line.split(':')
if not iface:
if line[0].strip() != 'lo':
device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0),
float(line[1].split()[8])/(1024.0*1024.0))
else:
if line[0].strip() == iface:
device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0),
float(line[1].split()[8])/(1024.0*1024.0))
return device_data
if __name__=='__main__':
parser = argparse.ArgumentParser(description='Network Interface Usage Monitor')
parser.add_argument('-i','--interface', dest='iface',
help='Network interface')
args = parser.parse_args()
netdevs = netdevs(iface = args.iface)
for dev in netdevs.keys():
print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))
当不带任何参数执行程序时,程序的行为与之前的版本完全一致。
然后,也可以指定感兴趣的网络设备。
例如:
复制代码 代码示例:
$ ./net_devs_2.py
em1: 0.0 MiB 0.0 MiB
wlan0: 146.099492073 MiB 12.9737148285 MiB
virbr1: 0.0 MiB 0.0 MiB
virbr1-nic: 0.0 MiB 0.0 MiB
$ ./net_devs_2.py --help
usage: net_devs_2.py [-h] [-i IFACE]
Network Interface Usage Monitor
optional arguments:
-h, --help show this help message and exit
-i IFACE, --interface IFACE
Network interface
$ ./net_devs_2.py -i wlan0
wlan0: 146.100307465 MiB 12.9777050018 MiB
九,脚本的系统范围可用性
在本文的帮助下,你可能已经可以写一个或多个有用的脚本,就像其它linux命令一样,你想要每天都使用它们。最简单的方式是将脚本设置为可执行的,然后为脚本设置一个BASH别名。你也可以移除.py扩展名,然后将脚本放在诸如/usr/local/sbin这样的标准位置。
十,其它有用的标准库模组
除了本文中已经提到的标准库模组,还有很多其它有用的标准模组:subprocess、ConfigParser、readline和curses。
接下来做什么?
在这个阶段,依靠你自己使用Python的经验,探索Linux内部,你可以参考下面的任一方式。
如果曾经需要写很多shell脚本/命令流水线来探索Linux内部,那么试一下Python。如果你想要一个更简单的方式来编写执行很多任务的实用程序脚本,那么试一下Python。
最后,如果你已经使用Python在linux操作系统上别写其它目的的程序,那么试一下用Python探索Linux内部。