# 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 日
© 允许规范转载