From 69e566ae0d4af4035ac7e2356ba38be2ac157ec8 Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Fri, 3 Apr 2026 17:09:24 +0100 Subject: [PATCH 1/4] Add .venv to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3c3629e64..671215ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +.venv From 87d907ba8c69c83082078bf2108165b0a69f0ebc Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Sun, 5 Apr 2026 14:33:34 +0100 Subject: [PATCH 2/4] feat: implement cat with -n and -b flags. --- implement-shell-tools/cat/cat.py | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 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..d75662f8b --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,41 @@ +import sys + +show_all_numbers = "-n" in sys.argv # this check will return true if found the flag +show_non_blank_numbers = "-b" in sys.argv # this check will return true if found the flag + +# 2. using filter get only the filenames everything except the script name and flags +files = [arg for arg in sys.argv[1:] if arg not in ["-n", "-b"]] + +if not files: + print("Usage: python3 cat.py [-n] [-b] ") + sys.exit() + +line_count = 1 +# 3. Process each file +for filename in files: + try: + with open(filename, "r") as file: + for line in file: + # Logic for -b + if show_non_blank_numbers: + if line.strip(): # If line is not empty + print(f"{line_count:>6}\t{line}", end="") + line_count += 1 + else: + # Standard cat no flags + print(line, end="") + + # Logic for -n + elif show_all_numbers: + print(f"{line_count:>6}\t{line}", end="") + line_count += 1 + + # Standard cat no flags + else: + print(line, end="") + + # throw clear errors + except FileNotFoundError: + print(f"Error: {filename}: No such file or directory") + except IsADirectoryError: + print(f"Error: {filename}: Is a directory") From 46111db0acb34087be4ad2ccd2e19a9d4a9d9511 Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Sun, 5 Apr 2026 15:17:00 +0100 Subject: [PATCH 3/4] feat: implement ls with -a and -1 flags. --- implement-shell-tools/ls/ls.py | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 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..549d8cbbe --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,45 @@ +import os +import sys + +# assign flags +show_all = "-a" in sys.argv +one_column = "-1" in sys.argv + +# Argument Filtering +args = [arg for arg in sys.argv[1:] if not arg.startswith("-")] +# if no path given we use the current path +path = args[0] if args else "." + +try: + # Get directory contents + entries = os.listdir(path) + + # Handle the -a flag + if show_all: + entries.extend([".", ".."]) + + # 5. Sort alphabetically, should used in ls.js as well + entries.sort() + + # Printing Logic + for entry in entries: + # Skip hidden files unless -a is passed + if not show_all and entry.startswith("."): + continue + + if one_column: + # -1 flag: Print vertically + print(entry) + else: + # Standard: Print horizontally with spaces + print(entry, end=" ") + + # newline only if we not print horizontally + if not one_column: + print() + +except FileNotFoundError: + print(f"ls: cannot access '{path}': No such file or directory") +except NotADirectoryError: + # if a file ls just prints the filename + print(path) From a9bbd5828a6bc7f9adfda28894d39eb80718503e Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Sun, 5 Apr 2026 16:20:21 +0100 Subject: [PATCH 4/4] feat: implement wc with -l -w -c flags and totals. --- implement-shell-tools/wc/wc.py | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 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..87a9445b5 --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,54 @@ +import sys +import os + +# Flag Detection +show_lines = "-l" in sys.argv +show_words = "-w" in sys.argv +show_chars = "-c" in sys.argv + +# If no flags then show all. +show_all = not (show_lines or show_words or show_chars) + +# Filter files excluding flags +files = [arg for arg in sys.argv[1:] if not arg.startswith("-")] + +# Total counters +total_l, total_w, total_c = 0, 0, 0 + +for filename in files: + try: + with open(filename, 'r') as f: + lines = f.readlines() + + # files calculation + l_count = len(lines) + w_count = sum(len(line.split()) for line in lines) + # os.path.getsize for bytes, in .js I used const bytes = Buffer.byteLength(content); + c_count = os.path.getsize(filename) + + # Update totals + total_l += l_count + total_w += w_count + total_c += c_count + + # Printing Logic + output = [] + if show_lines or show_all: output.append(f"{l_count:>8}") + if show_words or show_all: output.append(f"{w_count:>8}") + if show_chars or show_all: output.append(f"{c_count:>8}") + + print(f"{''.join(output)} {filename}") + + except FileNotFoundError: + print(f"wc: {filename}: No such file or directory") + except IsADirectoryError: + print(f"wc: {filename}: Is a directory") + print(f"{'0':>8} {'0':>8} {'0':>8} {filename}") + +# Print Total if there were multiple files +if len(files) > 1: + output_total = [] + if show_lines or show_all: output_total.append(f"{total_l:>8}") + if show_words or show_all: output_total.append(f"{total_w:>8}") + if show_chars or show_all: output_total.append(f"{total_c:>8}") + print(f"{''.join(output_total)} total")