diff --git a/implement-shell-tools/cat/mycat.py b/implement-shell-tools/cat/mycat.py new file mode 100644 index 00000000..3d4c9a20 --- /dev/null +++ b/implement-shell-tools/cat/mycat.py @@ -0,0 +1,39 @@ +import argparse +import glob + +parser = argparse.ArgumentParser( + prog="mycat", + description="Outputs the content of the given file(s), like the cat command", +) + +parser.add_argument("-n", help="Number all lines", action="store_true") +parser.add_argument("-b", help="Number all non-blank lines", action="store_true") +parser.add_argument("path", help="The file to display(allowing wildcards)", nargs="+") + +args = parser.parse_args() + +file_list = [] +for path in args.path: + file_list.extend(glob.glob(path)) + +line_number = 1 + +for filename in file_list: + try: + with open(filename, "r") as f: + for line in f: + stripped = line.rstrip("\n") + + if args.b: + if stripped: + print(f"{line_number:>6}\t{stripped}") + line_number += 1 + else: + print("") + elif args.n: + print(f"{line_number:>6}\t{stripped}") + line_number += 1 + else: + print(line, end="") + except FileNotFoundError: + print(f"mycat: {filename}: No such file or directory") diff --git a/implement-shell-tools/ls/myls.py b/implement-shell-tools/ls/myls.py new file mode 100644 index 00000000..69e2e678 --- /dev/null +++ b/implement-shell-tools/ls/myls.py @@ -0,0 +1,32 @@ +import argparse +import os + +parser = argparse.ArgumentParser( + prog="myls", + description="List directory contents like the ls command" +) + +parser.add_argument("-1", dest="one_per_line", action="store_true", + help="List one file per line") +parser.add_argument("-a", dest="all_files", action="store_true", + help="Include hidden files starting with '.'") +parser.add_argument("path", nargs="?", default=".", help="Directory to list (default: current directory)") + +args = parser.parse_args() + +try: + entries = os.listdir(args.path) +except FileNotFoundError: + print(f"my_ls: cannot access '{args.path}': No such file or directory") + exit(1) + +if not args.all_files: + entries = [e for e in entries if not e.startswith('.')] + +entries.sort() + +if args.one_per_line: + for entry in entries: + print(entry) +else: + print(" ".join(entries)) diff --git a/implement-shell-tools/wc/mywc.py b/implement-shell-tools/wc/mywc.py new file mode 100644 index 00000000..1540239e --- /dev/null +++ b/implement-shell-tools/wc/mywc.py @@ -0,0 +1,75 @@ +import argparse +import glob +import os + +def count_file(file_path, count_lines=False, count_words=False, count_bytes=False): + lines = words = bytes_count = 0 + with open(file_path, "r", encoding="utf-8", errors="ignore") as f: + for line in f: + lines += 1 + words += len(line.split()) + bytes_count = os.path.getsize(file_path) + + if not (count_lines or count_words or count_bytes): + return lines, words, bytes_count + + return ( + lines if count_lines else None, + words if count_words else None, + bytes_count if count_bytes else None + ) + + +def calculate_output(counts, flags, label): + """Helper to build consistent formatted output lines.""" + l_flag, w_flag, c_flag = flags + count_strings = [] + + # If no flags are set, show all counts + if not (l_flag or w_flag or c_flag): + lines, words, bytes_count = counts + count_strings = [f"{lines:>7}", f"{words:>7}", f"{bytes_count:>7}"] + else: + if counts[0] is not None: + count_strings.append(f"{counts[0]:>7}") + if counts[1] is not None: + count_strings.append(f"{counts[1]:>7}") + if counts[2] is not None: + count_strings.append(f"{counts[2]:>7}") + + return f"{' '.join(count_strings)} {label}" + + +def main(): + parser = argparse.ArgumentParser(description="count lines, words, and bytes like the wc command") + parser.add_argument("-l", action="store_true", help="Count lines") + parser.add_argument("-w", action="store_true", help="Count words") + parser.add_argument("-c", action="store_true", help="Count bytes") + parser.add_argument("path", nargs="+", help="Files to count") + + args = parser.parse_args() + + total_lines = total_words = total_bytes = 0 + file_list = [] + for pattern in args.path: + file_list.extend(glob.glob(pattern)) + + for file_path in file_list: + counts = count_file(file_path, args.l, args.w, args.c) + total_lines += counts[0] or 0 + total_words += counts[1] or 0 + total_bytes += counts[2] or 0 + + print(calculate_output(counts, (args.l, args.w, args.c), file_path)) + + if len(file_list) > 1: + total_counts = ( + total_lines if args.l or not (args.l or args.w or args.c) else None, + total_words if args.w or not (args.l or args.w or args.c) else None, + total_bytes if args.c or not (args.l or args.w or args.c) else None, + ) + print(calculate_output(total_counts, (args.l, args.w, args.c), "total")) + + +if __name__ == "__main__": + main()