前几天看到篇文章,里面有句话


python lsh python ls函数_迭代


刚看到这句话,在脑海里稍微构思了一下,嗯,ls实现起来很简单,但是实际动手时才发现真的不简单。python牛逼。

1. ls功能

Linux ls 命令用于显示指定工作目录下的内容。语法如下:

ls [-alhrt] [name]

这里只列举了几个常用的参数,这里列出的几个参数对应含义如下:

  1. -a:显示所有文件及目录;
  2. -l:将文件名称、文件大小、创建时间等信息列出;
  3. -h:个性化显示文件大小,如1K, 1G, 1T等;
  4. -r:将文件以相反次序排列;
  5. -t:将文件以修改时间次序排列。

2. 主要模块

主要使用的模块是argparsepathlib,其中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

python lsh python ls函数_Python_02