#!/usr/bin/python3 import os import sys import pwd import grp import time import argparse from enum import Enum # ANSI escape codes for colors COLORS = { 'RESET': '\033[0m', 'BOLD': '\033[1m', 'RED': '\033[91m', 'GREEN': '\033[92m', 'YELLOW': '\033[93m', 'BLUE': '\033[94m', 'MAGENTA': '\033[95m', 'CYAN': '\033[96m', 'WHITE': '\033[97m' } class Filetype(Enum): S_IFMT = 0o0170000 S_IFSOCK = 0o140000 S_IFLNK = 0o120000 S_IFREG = 0o100000 S_IFBLK = 0o060000 S_IFDIR = 0o040000 S_IFCHR = 0o020000 S_IFIFO = 0o010000 def colorize(text, color): return f"{COLORS[color]}{text}{COLORS['RESET']}" def color_file(text, mode): match Filetype(mode & Filetype.S_IFMT.value): case Filetype.S_IFREG: if mode & 0o100 or mode & 0o010 or mode & 0o001: return colorize(text, "GREEN") # file is executable, return green return colorize(text, "WHITE") case Filetype.S_IFDIR: return colorize(text, "BLUE") case Filetype.S_IFLNK: return colorize(text, "CYAN") case Filetype.S_IFSOCK: return colorize(text, "MAGENTA") case Filetype.S_IFBLK: return colorize(text, "YELLOW") case Filetype.S_IFCHR: return colorize(text, "YELLOW") case Filetype.S_IFIFO: return colorize(text, "MAGENTA") case _: return colorize(text, "RED") def parse_permission(mode): permissions = "-" match Filetype(mode & Filetype.S_IFMT.value): case Filetype.S_IFSOCK: permissions = 's' case Filetype.S_IFLNK: permissions = 'l' case Filetype.S_IFREG: permissions = '-' case Filetype.S_IFBLK: permissions = 'b' case Filetype.S_IFDIR: permissions = 'd' case Filetype.S_IFCHR: permissions = 'c' case Filetype.S_IFIFO: permissions = 'f' case _: permissions = '?' permissions += ''.join([ "r" if mode & 0o400 else "-", "w" if mode & 0o200 else "-", "x" if mode & 0o100 else "-", "r" if mode & 0o040 else "-", "w" if mode & 0o020 else "-", "x" if mode & 0o010 else "-", "r" if mode & 0o004 else "-", "w" if mode & 0o002 else "-", "x" if mode & 0o001 else "-" ]) return permissions def sizeof_fmt(num): for unit in ("", "K", "M", "G", "T", "P", "E", "Z"): if abs(num) < 1024.0: if unit == "": return f"{num}" #exception to not do .0 for bytes return f"{num:3.1f}{unit}" num /= 1024.0 return f"{num:.1f}Y" def list_files(directory='.', args={}): files = os.listdir(directory) print(f"files: {len(files)}") if args.time_sorted: files.sort(key=lambda name: os.lstat(name).st_mtime, reverse=(not args.reverse)) else: files.sort() # filter out hid files if needed if not args.all: files = [file for file in files if not file.startswith('.')] if args.long: for file in files: #file_path = os.path.join(directory, file) # for rel paths file_path = os.path.abspath(file) # for full path stats = os.lstat(file_path) mode = stats.st_mode permissions = parse_permission(mode) if args.sparse and permissions[0] == 'l': permissions = 'l ' nlink = stats.st_nlink user = pwd.getpwuid(stats.st_uid).pw_name group = grp.getgrgid(stats.st_gid).gr_name size = stats.st_size if args.human: size=sizeof_fmt(size) if args.sparse and Filetype(mode & Filetype.S_IFMT.value) == Filetype.S_IFDIR: size="" if args.full_time: mtime = time.strftime("%b %d %Y %X %z", time.localtime(stats.st_mtime)) # -0400 #mtime = time.strftime("%b %d %X %Z", time.localtime(stats.st_mtime)) # CST else: mtime = time.strftime("%b %d %H:%M", time.localtime(stats.st_mtime)) colored_file = color_file(file_path, mode) if os.path.islink(file_path): color = "RED" if os.path.exists(os.readlink(file_path)): color = "GREEN" print(f"{permissions} {nlink:2d} {user:<8s} {group:<8s} {size:>6} {mtime} {colored_file} -> {colorize(os.readlink(file_path), color)}") else: print(f"{permissions} {nlink:2d} {user:<8s} {group:<8s} {size:>6} {mtime} {colored_file}") else: for file in files: file_path = os.path.join(directory, file) if os.path.islink(file_path): print(colorize(file + " -> " + os.readlink(file_path), 'CYAN')) elif os.path.isdir(file_path): print(colorize(file, 'BLUE')) else: print(file) if __name__ == "__main__": parser = argparse.ArgumentParser(description="List files in a directory", add_help=False) parser.add_argument('directory', nargs='?', default='.', help="Directory path") parser.add_argument('-a', '--all', action='store_true', help="Show hidden files") parser.add_argument('-l', '--long', action='store_true', help="Use a long listing format") parser.add_argument('-F', '--full-time', action='store_true', help="Show full full time") parser.add_argument('-s', '--sparse', action='store_true', help="Blank irrelevant information") parser.add_argument('-t', '--time-sorted', action='store_true', help="Sort by time") parser.add_argument('-r', '--reverse', action='store_true', help="Reverse sort order") parser.add_argument('-h', '--human', action='store_true', help="Human Readable size") args = parser.parse_args() print(f"ls.py {' '.join(sys.argv[1:])}") list_files(args.directory, args)