From 7b4197c3d6f31f2eb8324cd210fd88841f025049 Mon Sep 17 00:00:00 2001 From: Ebrahim Beiaty Date: Fri, 20 Mar 2026 14:37:24 +0000 Subject: [PATCH 1/3] Implement cat shell tool in Python --- .../implement-shell-tools-python/cat/cat.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 implement-shell-tools/implement-shell-tools-python/cat/cat.py diff --git a/implement-shell-tools/implement-shell-tools-python/cat/cat.py b/implement-shell-tools/implement-shell-tools-python/cat/cat.py new file mode 100755 index 000000000..13797c2a1 --- /dev/null +++ b/implement-shell-tools/implement-shell-tools-python/cat/cat.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import sys +import os + +global_line_counter = 1 + +def print_file(file_path, options): + global global_line_counter + try: + with open(file_path, "r", encoding="utf-8") as f: + content = f.read() + + lines = content.split("\n") + if lines and lines[-1] == "": + lines.pop() + + for line in lines: + prefix = "" + + should_number = ( + options["number_mode"] == "all" or + (options["number_mode"] == "non-empty" and line.strip() != "") + ) + + if should_number: + prefix = f"{global_line_counter:6}\t" + global_line_counter += 1 + + sys.stdout.write(prefix + line + "\n") + + except Exception as e: + print(f"cat: {file_path}: {e}", file=sys.stderr) + sys.exit(1) + + +def main(): + global global_line_counter + args = sys.argv[1:] + + options = {"number_mode": "off"} + files = [] + + for arg in args: + if arg == "-n": + options["number_mode"] = "all" + elif arg == "-b": + options["number_mode"] = "non-empty" + else: + files.append(arg) + + if not files: + print("cat: missing file operand", file=sys.stderr) + sys.exit(1) + + for file_path in files: + global_line_counter = 1 + print_file(file_path, options) + + +if __name__ == "__main__": + main() From b547925a6cb92fd657991302ef88873267a1bd5d Mon Sep 17 00:00:00 2001 From: Ebrahim Beiaty Date: Fri, 20 Mar 2026 14:37:42 +0000 Subject: [PATCH 2/3] Implement ls shell tool in Python --- .../implement-shell-tools-python/ls/ls.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 implement-shell-tools/implement-shell-tools-python/ls/ls.py diff --git a/implement-shell-tools/implement-shell-tools-python/ls/ls.py b/implement-shell-tools/implement-shell-tools-python/ls/ls.py new file mode 100755 index 000000000..b86a58cfd --- /dev/null +++ b/implement-shell-tools/implement-shell-tools-python/ls/ls.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +import sys +import os + +def list_directory(path, show_all, one_per_line): + try: + entries = os.listdir(path) + if show_all: + # Add . and .. when -a is used + entries = [".", ".."] + entries + else: + entries = [e for e in entries if not e.startswith(".")] + + entries.sort(key=lambda x: (x not in (".", ".."), x)) + + for e in entries: + print(e) + + except Exception as e: + print(f"Error reading directory {path}: {e}", file=sys.stderr) + sys.exit(1) + + +def main(): + args = sys.argv[1:] + + show_all = "-a" in args + one_per_line = "-1" in args + + dirs = [a for a in args if a not in ("-a", "-1")] + + if not dirs: + dirs = [os.getcwd()] + + for i, path in enumerate(dirs): + if os.path.isdir(path): + if len(dirs) > 1: + print(f"{path}:") + list_directory(path, show_all, one_per_line) + if len(dirs) > 1 and i < len(dirs) - 1: + print("") + else: + print(path) + + +if __name__ == "__main__": + main() From d613e7a80e993791b0576df853d58e422a39da73 Mon Sep 17 00:00:00 2001 From: Ebrahim Beiaty Date: Fri, 20 Mar 2026 14:37:49 +0000 Subject: [PATCH 3/3] Implement wc shell tool in Python --- .../implement-shell-tools-python/wc/wc.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 implement-shell-tools/implement-shell-tools-python/wc/wc.py diff --git a/implement-shell-tools/implement-shell-tools-python/wc/wc.py b/implement-shell-tools/implement-shell-tools-python/wc/wc.py new file mode 100644 index 000000000..236d632c7 --- /dev/null +++ b/implement-shell-tools/implement-shell-tools-python/wc/wc.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import sys + +def count_file_content(content): + raw_lines = content.split("\n") + lines = len(raw_lines) - 1 if raw_lines[-1] == "" else len(raw_lines) + words = len(content.split()) + bytes_ = len(content.encode("utf-8")) + return lines, words, bytes_ + + +def print_counts(path, counts, options): + parts = [] + if options["line"]: + parts.append(str(counts[0]).rjust(8)) + if options["word"]: + parts.append(str(counts[1]).rjust(8)) + if options["byte"]: + parts.append(str(counts[2]).rjust(8)) + + output = "".join(parts) + print(f"{output} {path}") + + +def main(): + args = sys.argv[1:] + + options = {"line": False, "word": False, "byte": False} + files = [] + + for arg in args: + if arg == "-l": + options["line"] = True + elif arg == "-w": + options["word"] = True + elif arg == "-c": + options["byte"] = True + else: + files.append(arg) + + if not files: + print("No files specified", file=sys.stderr) + sys.exit(1) + + if not any(options.values()): + options = {"line": True, "word": True, "byte": True} + + total = [0, 0, 0] + multiple = len(files) > 1 + + for path in files: + try: + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + counts = count_file_content(content) + total = [t + c for t, c in zip(total, counts)] + + print_counts(path, counts, options) + + except Exception as e: + print(f"Error reading file {path}: {e}", file=sys.stderr) + sys.exit(1) + + if multiple: + print_counts("total", total, options) + + +if __name__ == "__main__": + main()