前几天看到篇文章,里面有句话
刚看到这句话,在脑海里稍微构思了一下,嗯,ls实现起来很简单,但是实际动手时才发现真的不简单。python牛逼。
1. ls功能
Linux ls 命令用于显示指定工作目录下的内容。语法如下:
ls [-alhrt] [name]
这里只列举了几个常用的参数,这里列出的几个参数对应含义如下:
-
-a:显示所有文件及目录;
-
-l:将文件名称、文件大小、创建时间等信息列出;
-
-h:个性化显示文件大小,如1K, 1G, 1T等;
-
-r:将文件以相反次序排列;
-
-t:将文件以修改时间次序排列。
2. 主要模块
主要使用的模块是argparse和pathlib,其中argparse模块能设置和接收命令行参数,也就使得Python对命令行的操作变得简单,而pathlib模块则用于处理文件路径,比os.path更优秀。这两个模块都是Python3.2版本后才有的。
既然要用Python实现ls,也就要在命令行中进行操作,比如python ls.py -a这样的命令,而对Python比较熟悉的人可能会想到使用sys模块来接收输入的命令,但使用argparse能让命令行操作变得更加简单!
3. 参数实现
1. 创建ArgumentParser对象
首先要导入模块并创建一个ArgumentParser对象,可以理解为一个解析器,然后就可以通过使用add_argument()方法为这个解析器添加参数了。
2. 位置(路径)参数
示例如下:
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() # 打印帮助
-
argparse 不仅仅做了参数的定义和解析, 还自动生成了帮助信息尤其是usage , 可以看到现在定义的参数是否是自己想要的;
-
-h是帮助信息,可有可无的;
-
考虑到ls基本功能是解决目录内容打印, 打印的时候应该指定目录的路径, 需要位置参数, 这里添加了位置参数path,是可选的位置参数, 没有提供参数就使用缺省值"." 表示当前路径;
-
args为参数列表,储存在了一个Namespace对象的属性上, 可以通过Namespace对象属性访问, 例如args.path; 运行结果:
Namespace(path='.') .
usage: ls [-h] [path]
list directory contents
positional arguments:
path path help
optional arguments:
-h, --help show this help message and exit
3. 选项参数
选项-l传参
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
-
通过parser.add_argument('-l', action='store_true')的action参数实现-l功能
-a、-h的选项传参也是一样实现过程
4. 功能实现
1. 实现-al
显示所有文件, 包括隐藏文件,以详细列表模式显示
from pathlib import Path
def list_dir(path: str = '.', all=False, detail=False, human=False):
def _show_dir(path: str = '.', all=False, detail=False, human=False):
#使用pathlib模块来操作路径文件
p = Path(path)
#当p为文件夹时,通过yield产生p文件夹下的所有文件、文件夹路径的迭代器
for file in p.iterdir():
if not all and str(file.name).startswith('.'):
continue
#判断-a参数是否为true
if detail:
st = file.stat()
#获取文件属主
owner, group = st.st_uid, st.st_gid
yield str((stat.filemode(st.st_mode), st.st_nlink, owner, group, str(h), datetime.datetime.fromtimestamp(st.st_atime).strftime('%Y-%m-%d %H:%M:%S'), file.name)).strip('()')
else:
yield str((file.name,)).strip('()')
yield from sorted(_show_dir(args.path, args.all, args.l, args.h), key=lambda x: x[-1])
-
yield from是Python3.3才出现的语法。yield from后面加上可迭代对象,他可以把可迭代对象里的每个元素一个一个的yield出来。(yield和yield from的区别,本篇暂时不提)
-
sorted(listdir(args.path, detail=True), key=lambda x: x[-1])把文件名按照升序排序输出
2. 实现-h
增加选项参数
parser.add_argument('-h', action='store_true', help='with -l, print sizes in human readable format ')
增加一个函数, 解决单位转换
def _convert_human(size: int):
units = ['', 'K', 'M', 'G', 'T', 'P']
depth = 0
while size >= 1024 and depth + 1 < len(units) :
size = size // 1024
depth += 1
return "{}{}".format(size, units[depth])
-
size大于1024且depth不是最后一个
在-l部分,增加处理
h = _convert_human(st.st_size)
3. 文件类型
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_block_device():
ret = 'b'
elif file.is_char_device():
ret = 'c'
elif file.is_dir():
ret = 'd'
elif file.is_file():
ret = '-'
else:
ret = '?'
return ret
4. 操作权限
def convert_mode(mode: int):
modelist = ['r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x']
m = mode & 0o777
modestr = bin(m)[-9:]
ret = ""
for i, v in enumerate(modestr):
if v == '1':
ret += modelist[i]
else:
ret += '-'
return ret
-
bin()返回一个整数int或者长整数long int的二进制表示
5. 效果
执行
ls.py -ahl /.../test
以上就是ls的以详细列表模式展示所有文件的基本功能实现。完整代码可关注公众号后台回复ls获取。
公众号:py知识库
id:py_talk