实现功能
按照文件名排序输出, 要求详细列表显示时, 时间可以按照"年-月-日 时:分:秒"格式显示, 如下图
-rw-rw-r– | 1 | python | python | 5 | 2019 - 5 - 7 00:07:00 | test.txt |
---|---|---|---|---|---|---|
mode | 硬链接 | 属主 | 属组 | 字节 | 时间 | 文件名 |
解决这个问题之前先了解下argparse 模块做起来更轻松
一个可执行文件或者脚本都可以接收参数
$ ls -l /etc
# /etc 是位置参数
# -l 是短选项
上例中, /etc 对应的就是位置参数, -l 是选项参数
import argparse
parser = argparse.ArgumentParser() # 获取参数解析器
args = parser.parse_args() # 分析参数
parser.print_help() # 打印帮助
运行结果
usage: Python.py [-h]
optional arguments:
-h, --help show this help message and exit
参数名称 | 说明 |
---|---|
prog | 程序的名字, 缺省使用sys.argv[0] 的basename |
add_help | 自动为解析器增加-h 和 --help 选项, 默认True |
description | 为程序增加描述 |
import argparse
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
args = parser.parse_args()
parser.print_help()
运行结果
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() # 打印帮助
运行结果
usage: ls [-h] path
ls: error: the following arguments are required: 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
上面的代码必须输入位置参数, 否则会报错
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, args.path) # 打印名词空间收集的参数
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 的实现
得到两次运行结果 :
Namespace(l='.', path='.') .
usage: ls [-h] [-l [L]] [path]
list directory contents
positional arguments:
path path help
optional arguments:
-h, --help show this help message and exit
-l [L] path help
raise ValueError("length of metavar tuple does not match nargs")
ValueError: length of metavar tuple does not match nargs
import argparse
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents') # 获取参数解析器
parser.add_argument('path', nargs='?', default='.', help='path help') # 增加缺省值路径, 帮助
parser.add_argument('-l', action='store_true')
args = parser.parse_args() # 分析参数, 同时传入可迭代参数
print(args)
parser.print_help() # 打印帮助
运行结果
Namespace(l=False, path='.')
usage: ls [-h] [-l] [path]
list directory contents
positional arguments:
path path help
optional arguments:
-h, --help show this help message and exit
-l
-a 的实现
无参数传入运行
import argparse
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help='dictory')
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()
运行结果
Namespace(all=False, l=False, path='.')
usage: ls [-h] [-l] [-a] [path]
list directory contents
positional arguments:
path dictory
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.
带参传入运行
import argparse
parser = argparse.ArgumentParser(prog='ls', add_help=True, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help='dictory')
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(('-l', '-a', '/tmp'))
print(args)
parser.print_help()
运行结果
Namespace(all=True, l=True, path='/tmp')
usage: ls [-h] [-l] [-a] [path]
list directory contents
positional arguments:
path dictory
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.
到目前为止, 已经解决了参数的定义和传参的问题, 下面解决下面问题
功能代码实现
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 : all or not f.name.startswith('.'), p.iterdir())
yield from map(str, filter(lambda f : all or not f.name.startswith('.'), p.iterdir()))
def _getfiletype(f:Path):
"""获取文件类型"""
if f.is_dir():
return 'd'
elif f.is_block_device():
return 'b'
elif f.is_char_device():
return 'c'
elif f.is_socket():
return 's'
elif f.is_symlink():
return 'l'
else:
return '-'
def listdirdetail(path, all=False):
"""详细列出本目录"""
p = Path(path)
for f in p.iterdir():
if not all and f.name.startswith('.'):
continue
stat = f.stat()
t = _getfiletype(f)
mode = oct(stat.st_mode)[-3:]
mtime = datetime.fromtimestamp(stat.st_mtime).strftime('%Y %m %d %H:%M:%S')
yield (t, mode, stat.st_nlink, stat.st_uid, stat.st_gid, stat.st_size, mtime, f.name)
modelist = dict(zip(range(9), ['r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x']))
# modelist = list('rwx'*3)
def _getmodestr(mode:int):
# 二进制位移
mstr = ''
for i in range(8, -1, -1):
mstr += modelist[8-i] if mode >> i & 1 else '-'
# mstr = ''
# for i, v in enumerate('{:09b}'.format(mode)[-9:]):
# mstr += modelist[i] if v == '1' else '-'
return mstr
合并列出文件的两个函数
listdirdetail 和 listdir 几乎一样, 太多重复, 考虑合并
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:
stat = i.stat()
mode = _getfiletype(i) + _getmodestr(stat.st_mode)
mtime = datetime.fromtimestamp(stat.st_mtime).strftime('%Y %m %d %H:%M:%S')
yield (mode, stat.st_nlink, stat.st_uid, stat.st_gid, stat.st_size, mtime, i.name)
排序
ls 的显示是把文件名按照升序排序输出
sorted(listdir(args.path, detail=True), key=lambda x: x[-1])
-h 的实现
-h , --human-readable, 如果-l 存在, -h 有效
parser.add_argument('-h', '--human-readable', action='store_true', help='with -l, print sizes in human readable format ')
def _gethuman(size:int):
units = ' KMGTP'
depth = 0
while size > 1000 and depth + 1 < len(units): # size大于1000且depth不是最后一个
size = size // 1000 # 若按照1024处理, 整除1024即可
depth += 1
return "{}{}".format(size, units[depth])
size = stat.st_size if not human else _gethuman(stat.st_size)
改进mode 的方法
import stat
from pathlib import Path
stat.filemode(Path().stat().st_mode)
其他的完善
uid、gid 的转换
import stat
import argparse
from pathlib import Path
from datetime import datetime
parser = argparse.ArgumentParser(prog='ls', add_help=False, description='list directory contents')
parser.add_argument('path', nargs='?', default='.', help='dictory')
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('-h', '--human-readable', action='store_true', help='with -l, print sizes in human readable format ', dest='human') # dest改变属性姓名
def listdir(path, all=False, detail=False, human=False):
def _gethuman(size:int):
units = ' KMGTP'
depth = 0
while size > 1000 and depth + 1 < len(units):
size = size // 1000
depth += 1
return "{}{}".format(size, units[depth])
def _listdir(path, all=False, detial=False, human=False):
p = Path(path)
for i in p.iterdir():
if not all and i.name.startswith('.'):
continue
if not detail:
yield (i.name,)
else:
st = i.stat()
# mode = _getfiletype(i) + _getmodestr(stat.st_mode)
mode = stat.filemode(st.st_mode)
mtime = datetime.fromtimestamp(st.st_mtime).strftime('%Y %m %d %H:%M:%S')
yield (mode, st.st_nlink, st.st_uid, st.st_gid, st.st_size, mtime, i.name)
yield from sorted(_listdir(path, all, detail, human), key=lambda x: x[-1])
if __name__ == '__main__':
args = parser.parse_args()
print(args)
parser.print_help()
files = listdir(args.path, args.all, args.l, args.human)
print(list(files))
运行结果
Namespace(all=True, human=True, l=True, path='.')
usage: ls [-l] [-a] [-h] [path]
list directory contents
positional arguments:
path dictory
optional arguments:
-l use a long listing format
-a, --all show all files, do not ignore entries starting with.
-h, --human-readable with -l, print sizes in human readable format
[('-rw-rw-rw-', 1, 0, 0, 2578, '2019 05 08 00:57:19', '*****.py'), ('-rw-rw-rw-', 1, 0, 0, 1804, '2019 05 06 15:20:51', '******.py'), ('-rw-rw-rw-', 1, 0, 0, 421, '2019 05 07 23:32:55', 'Python.py')]
测试
$ python xxx.py -lah
也可以在RUN -> Run / Debug Configurations -> Python
找到Paramters栏输入参数