建立命令行实用工具

[转]http://www.open-open.com/lib/view/open1375407327592.html
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