From a676909b2b073129ed21183b67e43226e18416e6 Mon Sep 17 00:00:00 2001 From: Priscilla Date: Wed, 13 Aug 2025 06:36:25 +0100 Subject: [PATCH 1/2] Python Implementation --- implement-shell-tools/cat/mycat.py | 39 ++++++++++++++++ implement-shell-tools/ls/myls.py | 32 +++++++++++++ implement-shell-tools/wc/mywc.py | 72 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 implement-shell-tools/cat/mycat.py create mode 100644 implement-shell-tools/ls/myls.py create mode 100644 implement-shell-tools/wc/mywc.py 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..5b9971f7 --- /dev/null +++ b/implement-shell-tools/wc/mywc.py @@ -0,0 +1,72 @@ +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 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) + + if not (args.l or args.w or args.c): + lines, words, bytes_count = counts + print(f"{lines:>7} {words:>7} {bytes_count:>7} {file_path}") + total_lines += lines + total_words += words + total_bytes += bytes_count + else: + count_strings = [] + if counts[0] is not None: + count_strings.append(f"{counts[0]:>7}") + total_lines += counts[0] + if counts[1] is not None: + count_strings.append(f"{counts[1]:>7}") + total_words += counts[1] + if counts[2] is not None: + count_strings.append(f"{counts[2]:>7}") + total_bytes += counts[2] + print(" ".join(count_strings), file_path) + + if len(file_list) > 1: + if not (args.l or args.w or args.c): + print(f"{total_lines:>7} {total_words:>7} {total_bytes:>7} total") + else: + total_strings = [] + if args.l: + total_strings.append(f"{total_lines:>7}") + if args.w: + total_strings.append(f"{total_words:>7}") + if args.c: + total_strings.append(f"{total_bytes:>7}") + print(" ".join(total_strings), "total") + +if __name__ == "__main__": + main() From 68ff24b332ee691a30b448648b494f570ed19a0c Mon Sep 17 00:00:00 2001 From: Priscilla Date: Sat, 18 Oct 2025 16:37:51 +0100 Subject: [PATCH 2/2] Removed duplicates in the file --- implement-shell-tools/wc/mywc.py | 61 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/implement-shell-tools/wc/mywc.py b/implement-shell-tools/wc/mywc.py index 5b9971f7..1540239e 100644 --- a/implement-shell-tools/wc/mywc.py +++ b/implement-shell-tools/wc/mywc.py @@ -19,6 +19,27 @@ def count_file(file_path, count_lines=False, count_words=False, count_bytes=Fals 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") @@ -35,38 +56,20 @@ def main(): 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 - if not (args.l or args.w or args.c): - lines, words, bytes_count = counts - print(f"{lines:>7} {words:>7} {bytes_count:>7} {file_path}") - total_lines += lines - total_words += words - total_bytes += bytes_count - else: - count_strings = [] - if counts[0] is not None: - count_strings.append(f"{counts[0]:>7}") - total_lines += counts[0] - if counts[1] is not None: - count_strings.append(f"{counts[1]:>7}") - total_words += counts[1] - if counts[2] is not None: - count_strings.append(f"{counts[2]:>7}") - total_bytes += counts[2] - print(" ".join(count_strings), file_path) + print(calculate_output(counts, (args.l, args.w, args.c), file_path)) if len(file_list) > 1: - if not (args.l or args.w or args.c): - print(f"{total_lines:>7} {total_words:>7} {total_bytes:>7} total") - else: - total_strings = [] - if args.l: - total_strings.append(f"{total_lines:>7}") - if args.w: - total_strings.append(f"{total_words:>7}") - if args.c: - total_strings.append(f"{total_bytes:>7}") - print(" ".join(total_strings), "total") + 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()