argparse模块
argparse 模块
一个可执行文件或脚本都可以接受参数
$ ls -l /etc
/etc 是位置参数
-l 是短选项
argparse 模块就提供了参数分析的功能
参数分类
- 位置参数:参数放在那里,就要对应一个参数位置。例如/etc/ 就是对应一个参数的位置
- 选项参数:必须通过前面是-的短选项或–的长选项,然后后面的才算该选项的参数,当然选项后面也可以没有参数
上例子中,/etc/对应的就是位置参数,-l对应的是选项参数
基本解析
import argparse
parser = argparse.ArgumentParser() #获得一个参数解析器
args = parser.parse_args() #分析参数
parser.print_help() #打印帮助
运行结果
usage: parser-test.py [-h]
optional arguments:
-h, --help show this help message and exit
Process finished with exit code 0
argparse不仅仅做了参数的定义和解析,还自动帮助生成了帮助信息。尤其是usage,可以看到现在定义的参数是否是自己想要的
解析器的参数
参数名称 | 说明 |
---|---|
prog | 程序的名字,缺省使用sys.argv[0]的basename |
add_help | 自动为解析器增加-h 和–help 选项,默认为True |
description | 为程序功能添加描述 |
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
usage: ls [-h]
list directory contents
optional arguments:
-h, --help show this help message and exit
位置参数解析
ls基本功能应该解决目录内容的打印
打印的时候应该指定目录路径,需要位置参数
import argparse
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path')
args = parser.parse_args() # 分析参数
parser.print_help() # 打印帮助
# 运行结果,出现了错误,提示需要输入path对应的位置参数
usage: ls [-h] path
ls: error: the following arguments are required: path
程序定义为:
ls [-h] path
-h为帮助选项,可有可无
path为位置参数,必须提供
传参
parse_args(args=None, namespace=None)
args 参数列表,一个可迭代对象。内部会把可迭代对象转换成list。如果None则使用命令行传入参数,非None则使用args参数的可迭代对象
import argparse
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path') # 位置参数
args = parser.parse_args(('/etc',)) # 分析参数,同时传入可迭代的参数
print(args, args.path) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
#运行结果
Namespace(path='/etc') /etc
usage: ls [-h] path
list directory contents
positional arguments:
path
optional arguments:
-h, --help show this help message and exit
Namespace(path=’/etc’) 里面的path 参数存储在了一个Namespace对象内的属性上,可以通过Namespace对象属性来访问,例如args.path
非必须位置参数
上面的代码必须输入位置参数,否则会报错
usage: ls [-h] path
ls: error: the following arguments are required: path
但有时候,ls命令不输入任何路径的话就表示列出当前目录的文件列表
import argparse
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help="path help") # 位置参数,可有可无,缺省值,帮助
args = parser.parse_args() # 分析参数,同时传入可迭代的参数
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
# 运行结果
Namespace(path='.')
usage: ls [-h] [path]
list directory contents
positional arguments:
path path help
optional arguments:
-h, --help show this help message and exit
可以看出path也变成可选的位置参数,没有提供就使用默认值 . 点号 表示当前路径
- help表示帮助文档中这个参数的描述
- nargs表示这个参数接受结果参数
- ?表示可有可无
- +表示至少一个
- * 可以任意个
- 数字表示必须是指定数目个
- default 表示如果不提供该参数,就使用这个值。一般和?、* 配合,因为他们都可以不提供位置参数,不提供就用缺省值
选项参数
-l 的实现
parser.add_argument('-l')
就增加了选项参数,参数定义为
ls [-h][-l L ] [path]
和我们要的形式有出入,期望的是[-l],如何解决?
nargs能否解决?
parser.add_argument(‘-l’,nargs=’?’)
ls [-h] [-l [L]] [path]
-l 还不是可选参数
那么,直接吧nargs=0,意思就是让这个选项接受0个参数,如下:
parser.add_argument('-l',nargs=0)
结果,抛出异常
raise ValueError(‘nargs for store actions must be > 0; if you ‘ValueError: nargs for store actions must be > 0; if you have nothing to store, actions such as store true or store const may be more appropriate
看来nargs是控制位置参数和选项参数的,不能控制选项参数的参数
这个问题使用action解决
parser.add_argument('-l',action="store_true")
看到命令定义变成了 ls [-h] [-l] [path]
提供-l 选项,例如
ls -l
得到Namespace(l=True, path=’.’),提供-l值是True
ls
得到Namespace(l=False, path=’.’),未提供-l值是False
这样同True、False来判断用户是否提供了该选项
parser.add_argument('-l', action='store_const', const = 20)
# 提供-l选项,属性值为20;否则,对应值为None
-a 的实现
parser.add_argument('-a', '--all', action='store_true') # 长短选项同时给
属性名称
参数都是Namespace 对象的属性,如果想指定这些属性名,可以使用dest
parser.add_argument('-l', action='store_true', dest='longfmt')
args = parser.parse_args(['/etc'])
print(args)
#输出
Namespace(longfmt=False, path='/etc')
import argparse
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list
directory contents')
parser.add_argument('path', nargs='?', default='.', help="directory") # 位置参数,可有可无,缺省值,帮助
parser.add_argument('-l', action='store_true', dest='longfmt', help='use a long listing format')
parser.add_argument('-a', '--all', action='store_true', help='show all files, do not ignore entries starting with .')
args = parser.parse_args() # 分析参数,同时传入可迭代的参数
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
# 运行结果
Namespace(all=False, longfmt=False, path='.')
usage: ls [-h] [-l] [-a] [path]
list directory contents
positional arguments:
path directory
optional arguments:
-h, --help show this help message and exit
-l use a long listing format
-a, --all show all files, do not ignore entries starting with .
# parser.parse_args('-l -a /tmp'.split())运行结果
Namespace(all=True, longfmt=True, path='/tmp')
练习:
ls业务功能的实现
实现ls命令功能,实现-l、-a和–all、-h选项
- 实现显示路径下的文件列表
- -a 和–all显示包含.开头的文件
- -l 详细列表显示
- -h 和 -l配合,人性化显示文件大小,例如1K,1G等
- 类型字符
- c字符
- d目录
- 普通文件
- l软连接
- b块设备
- s socket文件
- p pipe文件,即FIFO
参看Linux、Unix命令ls -lah
-rw-rw-r-- 1 python python 5 Oct 25 00:07 test4
mode 硬链接 属主 属组 字节 时间 文件名
按照文件名排序输出,可以和ls的顺序不一样,但要求文件名排序
要求详细列表显示时,时间可以按照“年-月-日 时:分:秒” 格式显示,例如2015-06-17 17:05:00
实现:
import argparse
from pathlib import Path
from datetime import datetime
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help="directory") # 位置
参数,可有可无,缺省值,帮助
parser.add_argument('-l', action='store_true', help='use a long listing format')
parser.add_argument('-a', '--all', action='store_true', help='show all files, do not ignore entries starting with .')
args = parser.parse_args() # 分析参数,同时传入可迭代的参数
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
def listdir(path, all=False):
"""列出本目录文件"""
p = Path(path)
# for f in p.iterdir():
# if not all and f.name.startswith('.'): # 不显示隐藏文件
# continue
# yield f.name
#yield from filter(lambda f:not(not all and f.name.startswith('.')), p.iterdir())
#yield from filter(lambda f: all or not f.name.startswith('.'), p.iterdir())
yield from map(lambda x: x.name, filter(lambda f:all or not f.name.startswith('.'), p.iterdir())) # 路径对象转成路径字符串
print(*(listdir(args.path)))
# -rw-rw-r-- 1 python python 5 Oct 25 00:07 test4
def listdirdetail(path, all=False):
"""详细列出目录"""
p = Path(path)
for f in p.iterdir():
if not all and f.name.startswith('.'): # 不显示隐藏文件
continue
# mode 硬链接 属主 属组 字节 时间 name
stat = f.stat()
mtime = datetime.fromtimestamp(stat.st_mtime).strftime('%Y %m %d %H:%M:%S')
yield (stat.st_mode, stat.st_nlink, stat.st_uid, stat.st_gid, st.st_size, mtime, f.name)
print(*(listdirdetail(args.path)))
mode是整数,八进制描述的权限,最终显示为rwx的格式。
import stat
mode = stat.filemode(st.st_mode) # 利用stat的函数
合并列出文件函数
listdirdetail 和listdir几乎一样,重复太多,合并
import argparse
from pathlib import Path
from datetime import datetime
import stat
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help="directory") # 位置参数,可有可无,缺省值,帮助
parser.add_argument('-l', action='store_true', help='use a long listing format')
parser.add_argument('-a', '--all', action='store_true', help='show all files, do not ignore entries starting with .')
args = parser.parse_args() # 分析参数,同时传入可迭代的参数
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
def listdir(path, all=False, detail=False):
"""详细列出本目录"""
p = Path(path)
for i in p.iterdir():
if not all and i.name.startswith('.'): # 不显示隐藏文件
continue
if not detail:
yield (i.name,)
else:
# -rw-rw-r-- 1 python python 5 Oct 25 00:07 test4
# mode 硬链接 属主 属组 字节 时间 name
st = i.stat()
mode = stat.filemode(st.st_mode)
mtime = datetime.fromtimestamp(st.st_atime).strftime('%Y/%m/%d %H:%M:%S')
yield (mode, st.st_nlink, st.st_uid, st.st_gid, st.st_size, mtime, i.name)
print(*listdir(args.path, detail=True), sep='\n')
排序
实现-r 选项,按文件名倒排
import argparse
from pathlib import Path
from datetime import datetime
import stat
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help="directory") # 位置参数,可有可无,缺省值,帮助
parser.add_argument('-l', action='store_true', help='use a long listing format')
parser.add_argument('-a', '--all', action='store_true', help='show all files, do not ignore entries starting with .')
parser.add_argument('-r', '--reverse', action='store_true', help="reverse order while sorting")
args = parser.parse_args() # 分析参数,同时传入可迭代的参数
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
def listdir(path, all=False, detail=False, reverse=False):
"""详细列出本目录"""
def _listdir(path, all, detail, reverse):
p = Path(path)
for i in p.iterdir():
if not all and i.name.startswith('.'): # 不显示隐藏文件
continue
if not detail:
yield (i.name,)
else:
# -rw-rw-r-- 1 python python 5 Oct 25 00:07 test4
# mode 硬链接 属主 属组 字节 时间 name
st = i.stat()
mode = stat.filemode(st.st_mode)
mtime = datetime.fromtimestamp(st.st_atime).strftime('%Y/%m/%d %H:%M:%S')
yield (mode, st.st_nlink, st.st_uid, st.st_gid, st.st_size, mtime, i.name)
return sorted(_listdir(path, all, detail, reverse), key=lambda x: x[len(x)-1], reverse=reverse)
print(*listdir(args.path, detail=True, reverse=True), sep='\n')
-h 的实现
-h ,–human-readable ,如果-l存在,-h有效。
1、增加选项参数
parser = argparse.ArgumentParser(prog='ls', description='list directory contents', add_help=False)
parser.add_argument('-h', '--human-readable', action='store_true', help='with -l, print sizes in human readable format')
2、增加一个函数,能够解决单位转换的
def _gethuman(size: int):
units = " KMGTP"
depth = 0
while size > 1000 and depth < len(units) - 1:
# 当前size大于1000,且depth不是最后一个进入循环
depth += 1
size //= 1000
return "{}{}".format(size, units[depth] if depth else '')
3、在-l逻辑部分增加处理
size = stat.st_size if not human else _gethuman(stat.st_size)
其他的完善
uid、gid的转换
pwd模块,The password database,提供访问Linux、Unix 的 password文件的方式。windows没有。
pwd.getpwuid(Path().stat().st_uid).pw_name
grp模块,Linux、Unix获取组信息的模块。windows没有
grp.getgrgid(Path().stat().st_gid).gr_name
pathlib模块,Path().group() 或者 Path().owner()也可以,本质上它们就是调用pwd模块和grp模块。
由于windows不支持,这次可以不加这个uid、gid的转换
最终代码
import argparse
from pathlib import Path
from datetime import datetime
import stat
# 获得一个参数解析器
parser = argparse.ArgumentParser(prog='ls', add_help=False, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help="directory") # 位置参数,可有可无,缺省值,帮助
parser.add_argument('-l', action='store_true', dest='long', help='use a long listing format')
parser.add_argument('-a', '--all', action='store_true', help='show all files, do not ignore entries starting with .')
parser.add_argument('-r', '--reverse', action='store_true', help="reverse order while sorting")
parser.add_argument('-h', '--human-readable', action='store_true',dest='human', help='with -l, print sizes in human readable format')
def _gethuman(size: int):
units = " KMGTP"
depth = 0
while size > 1000 and depth < len(units) - 1:
# 当前size大于1000,且depth不是最后一个进入循环
depth += 1
size //= 1000
return "{}{}".format(size, units[depth] if depth else '')
def _listdir(path, all, detail, reverse, human):
p = Path(path)
for i in p.iterdir():
if not all and i.name.startswith('.'): # 不显示隐藏文件
continue
if not detail:
yield (i.name,)
else:
# -rw-rw-r-- 1 python python 5 Oct 25 00:07 test4
# mode 硬链接 属主 属组 字节 时间 name
st = i.stat()
mode = stat.filemode(st.st_mode)
mtime = datetime.fromtimestamp(st.st_atime).strftime('%Y/%m/%d %H:%M:%S')
size = st.st_size if not human else _gethuman(st.st_size)
yield (mode, st.st_nlink, st.st_uid, st.st_gid, size, mtime, i.name)
def listdir(path, all=False, detail=False, reverse=False, human=False):
"""详细列出本目录"""
return sorted(_listdir(path, all, detail, reverse, human), key=lambda x: x[len(x)-1], reverse=reverse)
if __name__ == '__main__':
#args = parser.parse_args('-lrha'.split()) # 分析参数,同时传入可迭代的参数
args = parser.parse_args()
print(args) # 打印名词空间中收集的参数
parser.print_help() # 打印帮助
files = listdir(args.path, args.all, args.long, args.reverse, args.human)
print(*files, sep='\n')
测试
$ python xxx.py -lah -r
$ python xxx.py /etc/ -lahr