# coding: utf8
import argparse
import os
import time
from pathlib import Path
from sys import (stdout, stderr)
from functools import wraps


def catch(func):
    @wraps(func)
    def _catch(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
        except Exception as err:
            stderr.write(str(err) + '\n')
            exit(3)
        else:
            return result

    return _catch


def get_args():
    """
    获取命令行参数
    :return: args object
    """
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('-h', dest='human', help='使文件大小易读', action='store_true')
    parser.add_argument('-l', '--list', help='显示详细信息', action='store_true')
    parser.add_argument('-a', '--all', help='显示隐藏文件', action='store_true')

    parser.add_argument('path', nargs='*', default='.', type=str, help='文件或者目录路径,默认是当前目录')
    # 默认路径,路径可以指定多个
    # parser.add_argument('args', nargs=argparse.REMAINDER)
    # 将剩余选选项聚集到一个列表, 这个列表将被抛弃。都是无用参数
    parser.add_argument('--help', action='help')
    # 帮助
    return parser.parse_args()


def file_size(src):
    """
    文件大小转换为易读数值
    :param src: int 原始值
    :return: bytes
    """

    def _tran(number, typ):
        return '{}{}'.format(number, typ).encode()

    if src < 1024:
        return str(src).encode()

    if round(src / (1024 ** 3), 1) < 1024:
        if round(src / 1024, 1) < 1024:
            return _tran(round(src / 1024, 1), 'K')
        elif round(src / 1024 ** 2, 1) < 1024:
            return _tran(round(src / (1024 ** 2), 1), 'M')
        else:
            return _tran(round(src / (1024 ** 3), 1), 'G')
    else:
        if round(src / (1024 ** 4), 1) < 1024:
            return _tran(round(src / (1024 ** 4), 1), 'T')
        else:
            return _tran(round(src / (1024 ** 5), 1), 'P')


def file_type(path_obj):
    if path_obj.is_file():
        return b'-'
    if path_obj.is_dir():
        return b'd'
    if path_obj.is_char_device():
        return b'c'
    if path_obj.is_fifo():
        return b'p'
    if path_obj.is_block_device():
        return b'b'
    if path_obj.is_socket():
        return b's'
    if path_obj.is_symlink():
        return b'l'


def get_time(timestamp):
    return time.strftime("%Y-%m-%d %H:%M", time.localtime(timestamp)).encode()


def purview(n):
    """
    权限数字转换为字符
    :param n: st_mode
    :return: bytes
    """
    _purview = n & 0o777
    _string = bytearray()

    # 获取权限
    def _transform(number):
        """
        数字转换为权限 rwx
        :param number: 十进制数据 0-7
        :return: bytes
        """
        index = 2
        result = bytearray()
        for s in b'rwx':
            result.append(s if (number >> index & 1) else 0x2D)
            index -= 1
        return result

    def _transform1(number):
        """
        数字转换为权限 rwx
        效率稍微高于 _transform
        :param number: 十进制数据 0-7
        :return: bytes
        """
        string = b'rxw'
        return bytearray(string[_ - 2] if (number >> _ & 1) else 0x2D for _ in range(2, -1, -1))

    for i in range(6, -1, -3):
        if i == 6:
            tmp = _purview >> i
        else:
            tmp = _purview >> i & 0o7
        _string.extend(_transform1(tmp))
    return _string


def get_user(uid):
    """
    获取用户名
    :param uid: uid
    :return: username
    """
    uid = str(uid)
    name_file = '/etc/passwd'
    if os.name == 'posix' and Path(name_file).exists():
        with open(name_file, encoding='utf8') as file:
            for line in file.readlines():
                if uid in line:
                    return line.split(':')[0]
    return uid


def get_group(gid):
    """
    获取用户组
    :param gid: gid
    :return: group name
    """
    gid = str(gid)
    group_file = '/etc/group'
    if os.name == 'posix' and Path(group_file).exists():
        with open(group_file, encoding='utf8') as file:
            for line in file.readlines():
                if gid in line:
                    return line.split(':')[0]
    return gid


def detailed(path_obj, size=False):
    """
    详细显示文件信息
    :param path_obj: 路径对象
    :param size: 是否使文件大小易读
    :return: string
    """
    stat = path_obj.stat()
    string = [
        (file_type(path_obj) + purview(stat.st_mode)).decode(),  # 文件类型和权限
        str(stat.st_nlink),  # 硬链接
        get_user(stat.st_uid),  # uid or user name
        get_group(stat.st_gid),  # gid or group name
        file_size(stat.st_size).decode() if size else str(stat.st_size),  # 文件大小
        get_time(stat.st_ctime).decode(),  # 创建时间
        path_obj.name if path_obj.name else '.'  # 文件名
    ]
    return '\t'.join(string) + '\n'


@catch
def main():
    args = get_args()
    code = 0
    flag = False

    for path in args.path:
        path_obj = Path(path)
        if not path_obj.exists():
            # 如果文件不存在
            stderr.write('{}: No Such File Or Directory.'.format(path) + '\n')
            code = 2
            continue
        if path_obj.is_dir():
            # 如果是个文件夹
            files_list = ['.', '..']
            if flag:
                # 如果之前有文件出现过
                stdout.write('\n{}:\n'.format(path_obj.name))

            files_list += [i.name for i in path_obj.iterdir()]
            files_list.sort(key=str)
            if not args.all:
                # 不要显示隐藏文件
                files_list = [i for i in files_list if i[0] != '.']

            if args.list:
                # 如果显示了详细信息
                for file in files_list:
                    _path_obj = path_obj / file
                    # 文件path对象
                    stdout.write(detailed(_path_obj, args.human))
            else:
                stdout.write('\t'.join(files_list) + '\n')
        else:
            # 如果不是一个目录
            flag = True
            if args.list:
                # 如果需要显示详细信息
                stdout.write(detailed(path_obj, size=args.human))
                continue
            stdout.write(path_obj.name)
            stdout.write('\t')
    if flag:
        stdout.write('\n')
    return code


if __name__ == '__main__':
    exit(main())
最后修改:2020 年 09 月 07 日 02 : 20 PM
如果觉得我的文章对你有用,请随意赞赏