From 4c6c445bb4af9beff1df796f33da7db839592a9d Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Thu, 7 Aug 2025 17:20:27 +0100 Subject: [PATCH 1/6] cat on its own first --- implement-shell-tools/cat/cat.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 implement-shell-tools/cat/cat.py diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py new file mode 100644 index 000000000..a7de5cfe7 --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,17 @@ +import argparse + +def main(): + parser = argparse.ArgumentParser(description="Basic cat command") + parser.add_argument("files", nargs="+", help="Files to read") + args = parser.parse_args() + + for filename in args.files: + try: + with open(filename, "r") as f: + for line in f: + print(line, end="") + except FileNotFoundError: + print(f"my_cat: {filename}: No such file or directory") + +if __name__ == "__main__": + main() \ No newline at end of file From 6d077e89421f58909071bc422af1aaf9d20fec15 Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Thu, 7 Aug 2025 17:23:43 +0100 Subject: [PATCH 2/6] cat -n --- implement-shell-tools/cat/cat.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py index a7de5cfe7..f6b95b66f 100644 --- a/implement-shell-tools/cat/cat.py +++ b/implement-shell-tools/cat/cat.py @@ -1,17 +1,25 @@ import argparse def main(): - parser = argparse.ArgumentParser(description="Basic cat command") + parser = argparse.ArgumentParser(description="cat with -n option") + parser.add_argument("-n", action="store_true", help="number all output lines") parser.add_argument("files", nargs="+", help="Files to read") args = parser.parse_args() + line_number = 1 for filename in args.files: try: with open(filename, "r") as f: for line in f: - print(line, end="") + # Remove trailing newline for formatting + line_to_print = line.rstrip('\n') + if args.n: + print(f"{line_number:6}\t{line_to_print}") + line_number += 1 + else: + print(line, end="") except FileNotFoundError: - print(f"my_cat: {filename}: No such file or directory") + print(f"cat: {filename}: No such file or directory") if __name__ == "__main__": - main() \ No newline at end of file + main() From 19bbd49d349c38820bcf4783ed8820e6ff6d2e58 Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Thu, 7 Aug 2025 22:31:44 +0100 Subject: [PATCH 3/6] cat -b --- implement-shell-tools/cat/cat.py | 41 +++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py index f6b95b66f..031b6ef4b 100644 --- a/implement-shell-tools/cat/cat.py +++ b/implement-shell-tools/cat/cat.py @@ -1,23 +1,46 @@ import argparse +import glob def main(): - parser = argparse.ArgumentParser(description="cat with -n option") - parser.add_argument("-n", action="store_true", help="number all output lines") - parser.add_argument("files", nargs="+", help="Files to read") + parser = argparse.ArgumentParser(description="cat with -n and -b options") + parser.add_argument("-n", action="store_true", help="Number all output lines") + parser.add_argument("-b", action="store_true", help="Number non-blank lines (overrides -n)") + parser.add_argument("files", nargs="+", help="Files to read (supports *.txt)") args = parser.parse_args() + # -b overrides -n + number_all = args.n + number_nonblank = args.b + if number_all and number_nonblank: + number_all = False + + # Expand wildcard patterns (e.g. *.txt) + file_list = [] + for pattern in args.files: + matched_files = glob.glob(pattern) + if matched_files: + file_list.extend(matched_files) + else: + file_list.append(pattern) + + # Print contents of each file line_number = 1 - for filename in args.files: + for filename in file_list: try: with open(filename, "r") as f: for line in f: - # Remove trailing newline for formatting - line_to_print = line.rstrip('\n') - if args.n: - print(f"{line_number:6}\t{line_to_print}") + text = line.rstrip("\n") + if number_nonblank: + if text.strip(): # only number non-blank lines + print(f"{line_number:6}\t{text}") + line_number += 1 + else: + print() + elif number_all: + print(f"{line_number:6}\t{text}") line_number += 1 else: - print(line, end="") + print(text) except FileNotFoundError: print(f"cat: {filename}: No such file or directory") From e8ac9af802c8f53f94a28ddbe985ac07e3612836 Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Thu, 7 Aug 2025 22:31:49 +0100 Subject: [PATCH 4/6] ls --- implement-shell-tools/ls/ls.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 implement-shell-tools/ls/ls.py diff --git a/implement-shell-tools/ls/ls.py b/implement-shell-tools/ls/ls.py new file mode 100644 index 000000000..7bc1451f6 --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,23 @@ +import argparse +import os + +parser = argparse.ArgumentParser(description="Display one file per line from ls directory") + +parser.add_argument("-1", dest="one", action="store_true", help="List one file per line") +parser.add_argument("-a", action="store_true", help="Include hidden files (those starting with .)") +parser.add_argument("path", nargs="?", default=".", help="The directory to list (default: current directory)") + +args = parser.parse_args() + + +files = sorted(os.listdir(args.path)) + + +if not args.a: + files = [f for f in files if not f.startswith(".")] + +if args.one: + for f in files: + print(f) +else: + print(" ".join(files)) From 17b2b5becb489d27a34db586930c1fe6e2ac76ec Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Thu, 7 Aug 2025 22:34:48 +0100 Subject: [PATCH 5/6] basic (wc) --- implement-shell-tools/wc/wc.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 implement-shell-tools/wc/wc.py diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py new file mode 100644 index 000000000..e44cbc290 --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,25 @@ +import argparse + +def count_file(filename): + try: + with open(filename, "r", encoding="utf-8") as f: + content = f.read() + lines = content.count("\n") + words = len(content.split()) + bytes_ = len(content.encode("utf-8")) + return lines, words, bytes_ + except FileNotFoundError: + print(f"wc: {filename}: No such file or directory") + return 0, 0, 0 + +def main(): + parser = argparse.ArgumentParser(description="Python version of wc") + parser.add_argument("files", nargs="+", help="Files to count") + args = parser.parse_args() + + for filename in args.files: + lines, words, bytes_ = count_file(filename) + print(f"{lines:7} {words:7} {bytes_:7} {filename}") + +if __name__ == "__main__": + main() From 6c1e6a5fbfe91ae927ef2cc3ef6e9647263f18ed Mon Sep 17 00:00:00 2001 From: aiah ibrahim Date: Fri, 8 Aug 2025 11:36:18 +0100 Subject: [PATCH 6/6] wc --- implement-shell-tools/wc/wc.py | 73 +++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py index e44cbc290..224ab13f3 100644 --- a/implement-shell-tools/wc/wc.py +++ b/implement-shell-tools/wc/wc.py @@ -1,25 +1,78 @@ import argparse +import glob def count_file(filename): + line_count = 0 + word_count = 0 + byte_count = 0 + try: - with open(filename, "r", encoding="utf-8") as f: + with open(filename, "rb") as f: content = f.read() - lines = content.count("\n") - words = len(content.split()) - bytes_ = len(content.encode("utf-8")) - return lines, words, bytes_ + byte_count = len(content) + text = content.decode("utf-8", errors="replace") + lines = text.splitlines() + line_count = len(lines) + word_count = sum(len(line.split()) for line in lines) + + return (line_count, word_count, byte_count) except FileNotFoundError: print(f"wc: {filename}: No such file or directory") - return 0, 0, 0 + return None def main(): parser = argparse.ArgumentParser(description="Python version of wc") - parser.add_argument("files", nargs="+", help="Files to count") + 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("files", nargs="+", help="Files to read (supports wildcards)") args = parser.parse_args() - for filename in args.files: - lines, words, bytes_ = count_file(filename) - print(f"{lines:7} {words:7} {bytes_:7} {filename}") + # If no flag is given, show all + show_lines = args.l + show_words = args.w + show_bytes = args.c + if not (show_lines or show_words or show_bytes): + show_lines = show_words = show_bytes = True + + file_list = [] + for pattern in args.files: + matched = glob.glob(pattern) + if matched: + file_list.extend(matched) + else: + file_list.append(pattern) + + total_lines = total_words = total_bytes = 0 + for filename in file_list: + result = count_file(filename) + if result is None: + continue + lines, words, bytes_ = result + total_lines += lines + total_words += words + total_bytes += bytes_ + + output = [] + if show_lines: + output.append(f"{lines:7}") + if show_words: + output.append(f"{words:7}") + if show_bytes: + output.append(f"{bytes_:7}") + output.append(filename) + print(" ".join(output)) + + if len(file_list) > 1: + output = [] + if show_lines: + output.append(f"{total_lines:7}") + if show_words: + output.append(f"{total_words:7}") + if show_bytes: + output.append(f"{total_bytes:7}") + output.append("total") + print(" ".join(output)) if __name__ == "__main__": main()