beijixs 发表于 2020-9-9 12:01

利用Python实现Linux的ls命令 -l -h -a选项

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

"""
import stat
stat.file_mode(mode:int)
获取文件权限,直接转换为字符 'rwxrwxrwx'
"""


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, prog='ls', description='显示目录或文件')
    # prog 程序名称
    # add_help 是否为其添加一个 -h --help
    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='文件或者目录路径,默认是当前目录')
    # nargs? 可有可无 ,没有需要default
    # nargs* 多个参数保存为一个列表
    # nargs+ 至少一个
    # 默认路径,路径可以指定多个
    # parser.add_argument('args', nargs=argparse.REMAINDER)
    # 将剩余选选项聚集到一个列表, 这个列表将被抛弃。都是无用参数
    parser.add_argument('--help', action='help')
    # 帮助
    return parser.parse_args()


def get_files(path_obj: Path, hide: bool = False):
    """
    文件名生成器
    :param path_obj: Path object
    :param hide: 是否添加隐藏文件
    :return:
    """
    # if hide:
    #   yield '.'
    #   yield '..'
    #   # 如果需要显示隐藏文件
    for file in path_obj.iterdir():
      if not hide and file.name.startswith('.'):
            # 如果hide为False,文件开头包含'.' 直接跳过
            continue
      yield file.name


def file_size(size):
    """
    文件大小转换为易读数值
    :param size: int 原始值
    :return: str
    """
    # 考虑为深度问题
    units = ['', 'K', 'M', 'G', 'T', 'P']
    depth = 0
    while size >= 1024:
      size = round(size / 1024, 1)
      depth += 1
    return '{}{}'.format(size, units)


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


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

    # 获取权限
    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

    string = 'rxw'
    for i in range(6, -1, -3):
      if i == 6:
            tmp = _purview >> i
      else:
            tmp = _purview >> i & 0o7
      _string += ''.join( if (tmp >> _ & 1) else 0x2D for _ in range(2, -1, -1)])
    return _string


# def get_mode(n):
#   """
#   权限数字转换为字符
#   :param n: st_mode
#   :return: bytes
#   """
#   _purview = n & 0o777
#   mode_list = {0: 'r', 1: 'w', 2: 'x', 3: 'r', 4: 'w', 5: 'x', 6: 'r', 7: 'w', 8: 'x'}
#   ret = ''
#   for i in range(8, -1, -1):
#         if _purview >> i & 1:
#             ret += mode_list
#         else:
#             ret += '-'


def get_mode1(mode: int):
    """
    获取权限, 效率优先于get_mode
    :param mode:
    :return: string
    """
    mode_list = ['r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x']
    mode_str = bin(mode)[-9:]# 转为二进制,然后字符串
    ret = ''
    for index, flag in enumerate(mode_str):
      if flag == '1':
            ret += mode_list
      else:
            ret += '-'
    return ret


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(':')
    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(':')
    return gid


def detailed(path_obj, size=False):
    """
    详细显示文件信息
    :param path_obj: 路径对象
    :param size: 是否使文件大小易读
    :return: string
    """
    stat = path_obj.stat()
    string = [
      # file_type(path_obj).decode() + get_mode(stat.st_mode),# 文件类型和权限
      file_type(path_obj) + get_mode1(stat.st_mode),# 文件类型和权限
      str(stat.st_nlink),# 硬链接
      get_user(stat.st_uid),# uid or user name
      # path_obj.owner(),# linux 支持的用户名获取
      # path_obj.group(),# linux 支持的用户名获取
      get_group(stat.st_gid),# gid or group name
      file_size(stat.st_size) if size else str(stat.st_size),# 文件大小
      # get_time(stat.st_ctime).decode(),# 创建时间
      time.strftime("%Y-%m-%d %H:%M", time.localtime(stat.st_mtime)),# 修改时间
      # datetime.datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M'),# 修改时间
      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():
            # 如果是个文件夹
            if flag:
                # 如果之前有文件出现过
                stdout.write('\n{}:\n'.format(path_obj.name))

            files_list =
            files_list.sort(key=str)

            if args.list:
                # 如果显示了详细信息
                for file in files_list:
                  _path_obj = path_obj / file
                  # 文件path对象
                  stdout.write(detailed(_path_obj, args.human))
            else:
                stdout.write('    '.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())

wushengli 发表于 2020-9-9 12:54

感谢分享!辛苦辛苦。

LThranduil 发表于 2020-9-9 12:58

谢谢楼主分享

zj1977lsz 发表于 2020-9-9 14:05

太复杂了,看不太懂!

Catch 发表于 2020-9-9 14:52

230行竟然把我@过来了。。
页: [1]
查看完整版本: 利用Python实现Linux的ls命令 -l -h -a选项