[toc]
python 日志处理一
练习
- 实现ls 命令功能,实现-l、-a和 -all、 -h 选项
- 实现显示路径下的文件列表;
- -a和 -all显示包含.开头的文件;
- -l 详细列表显示;
- -h 和 -l 配合,人性化显示文件大小,例如1K、1G、1T等。可以认为1G=1000M;
- c 字符;d目录;-普通文件;l软链接;b块设备;s socket文件;p pip文件,即FIFO;
- -rw-rw-r— 1 python python 5 Oct 00:07 test4;
- mode 硬链接 属主 属组 字节 时间 文件名;
- 按照文件名排序输出,可以和ls的顺序不一样,但要求文件名排序;
- 要求详细列表显示时,时间可以按照”年-月-日 时:分:秒” 格式显示。
* argparse 模块
一个可执行文件或者脚本可以接收参数;
# ls -l /etc /etc # 是位置参数 -l # 是短选项
如何把这些参数传递给程序呢 ?
从3.2开始Python提供了参数分析的argparse。
- 参数分类
位置参数放在哪里,就要对应一个参数位置。例如/etc 就是对应一个参数位置;
选项参数,前面必须是通过-
的短选项,或者--
长选项, 然后后面的才算它的参数,当然短选项后面也可以没有参数;
上述中 ‘/etc’ 对应的是位置参数, -l 是选项参数;
基本目录的实现
#!/usr/bin/env python from pathlib import Path def showdir(path:str='.'): p = Path(path) for file in p.iterdir(): print(file.name) if __name__ == '__main__': showdir('/etc')
基本的解析
先来看一段最简单的程序
#!/usr/bin/env python import argparse parser = argparse.ArgumentParser() # 获得一个参数解析器 args = parser.parse_args() # 分析参数 parser.help() # 打印帮助
打印结果
usage: if_not.py [-h] optional arguments: -h, --help show this help message and exit
argparse 不仅仅做了参数的定义和解析,还自动帮助生成了帮助信息。尤其是usage,可以看到定义参数是否是自己想要的;
解析器的参数
prog 程序的名字,缺省使用sys.argv[0]
add_help 自动解析增加-h 和—help 的选项,默认为True
description 为程序功能添加描述parser = argparse.ArgumentParser(prog='ls',add_help=True,description='list directory contents')
位置参数解析
ls 基本功能应该解决目录内容的打印;
打印的时候应该指定目录的路径,需要位置参数
import argparse # 获得一个参数解析器 parser - argparse.ArgumentParser(prog='ls',add_help=True,description='list direct ory contents') parser.add_argument('path') args = parser.parse_args() # 分析参数 parser.print_help() # 打印帮助
程序等定义为:ls [-h] path
-h为帮助,可有可无
path 为位置参数,必须提供
传参
parse_args(args=None, naespace=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) # 打印名词空间中收集的参数 parser.print_help() #打印帮助 运行结果 Namespace(path='/etc') usage: ls [-h] path list directory contents positional arguents path optional arguments -h, --help show this help message and exit
ls: error: the following arguments are required: path
但有时候,ls命令不输入任何路径的话就表示列出当前目录的文件列表。
#!/usr/bin/env python from pathlib import Path import argparse def showdir(path:str='.'): p = Path(path) for file in p.iterdir(): print(file.name) parser = argparse.ArgumentParser(prog='ls', add_help=False, description='list all files') # 构建解析器 parser.add_argument('path', nargs='?',default='.',help='path help') # 位置参数 parser.add_argument('-l',action='store_true') parser.add_argument('-h',action='store_true') parser.add_argument('-a','--all',action='store_true') if __name__ == '__main__': args = parser.parse_args(('/etc','-l')) parser.print_help() print('args=', args) print(args.path, args.l, args.h, args.all)
实现
#!/usr/bin/env python from pathlib import Path import datetime import stat def convert_mode(mode:int): modelist = ['r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x'] modestr = bin(mode)[-9:] # '664' # 110110100 ret = "" for i,c in enumerate(modestr): if c == '1': ret += modelist[i] else: ret += '-' return ret # d - s p l c b def convert_type(file:Path): ret ="" if file.is_symlink(): ret = 'l' elif file.is_fifo(): ret = 'p' elif file.is_socket(): ret = 's' elif file.is_char_device(): ret = 'c' elif file.is_block_device(): ret = 'b' elif file.is_dir(): ret = 'd' else: ret = '-' return ret import argparse def showdir(path,all=False, detail=False, human=False): p = Path(path) for file in p.iterdir(): if not all and str(file.name).startswith('.'): # .开头不打印 --all continue if detail: st = file.stat() ## 使用自建convert_mode 函数 # yield (convert_type(file) + convert_mode(st.st_mode), st.st_nlink, st.st_uid, st.st_gid, st.st_size, # datetime.datetime.fromtimestamp(st.st_atime).strftime('%Y-%m-%d %H:%M:%S'), # file.name) ## 使用 stat.filemode 函数 yield (convert_type(file) + stat.filemode(st.st_mode), st.st_nlink, st.st_uid, st.st_gid, st.st_size, datetime.datetime.fromtimestamp(st.st_atime).strftime('%Y-%m-%d %H:%M:%S'), file.name) else: yield file.name parser = argparse.ArgumentParser(prog='ls', add_help=False, description='list all files') # 构建解析器 parser.add_argument('path', nargs='?',default='.',help='path help') # 位置参数 parser.add_argument('-l',action='store_true') parser.add_argument('-h',action='store_true') parser.add_argument('-a','--all',action='store_true') if __name__ == '__main__': args = parser.parse_args(('/etc','-l')) parser.print_help() print('args=', args) # print(args.path, args.l, args.h, args.all) showdir(args.path) for st in showdir(args.path,args.all, args.l, args.h): print(st)
优化
#!/usr/bin/env python from pathlib import Path import argparse import datetime import stat def listdir(path='.', all=False, detail=False, human=False): def _get_human(size :int): units = ['B', 'K', 'M', 'G', 'T', 'P'] depth = 0 while size >= 1000: size = size // 1000 depth += 1 return "{} {}".format(size, units[depth]) def _showdir(path='.', all=False, detail=False, human=False): p = Path(path) for file in p.iterdir(): if not all and str(file.name).startswith('.'): # .开头 不打印 --all continue # -l if detail: st = file.stat() # -rw-rw-r-- 1 python python 2920 Sep 21 07:30 ips h = str(st.st_size) if human: h = _get_human(st.st_size) yield (stat.filemode(st.st_mode), st.st_nlink, st.st_uid, st.st_gid, h, datetime.datetime.fromtimestamp(st.st_atime).strftime('%Y-%m-%d %H:%M:%S'),file.name) else: # 没有 -l yield(file.name) yield from sorted(_showdir(args.path, args.all, args.l ,args.h ), key=lambda x: x[len(x) -1]) # ls [path] [-l] [-a] [-h] parser = argparse.ArgumentParser(prog='ls', add_help=False, description='list all files') #构造解析器 parser.add_argument('path',nargs='?',default='.',help='path help') # 位置参数 parser.add_argument('-l',action='store_true') parser.add_argument('-h',action='store_true') parser.add_argument('-a','--all',action='store_true') if __name__ == '__main__': args = parser.parse_args() parser.print_help() print('args=', args) for st in listdir(args.path,args.all, args.l ,args.h ): print(st)
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 depth += 1 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 # mode 硬链接 属主 属组 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')