From 69e566ae0d4af4035ac7e2356ba38be2ac157ec8 Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Fri, 3 Apr 2026 17:09:24 +0100 Subject: [PATCH 1/5] 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/5] 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/5] 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/5] 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") From c65e4d6aad437caf9da32c3b6b5c473b3ad313ac Mon Sep 17 00:00:00 2001 From: Jamal Laqdiem Date: Thu, 4 Jun 2026 11:12:42 +0100 Subject: [PATCH 5/5] Fix: update the files to use arparse library to replace the manual string filtrening --- implement-shell-tools/cat/cat.py | 23 ++++++++++++++++++----- implement-shell-tools/ls/ls.py | 28 +++++++++++++++++++--------- implement-shell-tools/wc/wc.py | 26 ++++++++++++++++++++------ 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py index d75662f8b..5b26b1574 100644 --- a/implement-shell-tools/cat/cat.py +++ b/implement-shell-tools/cat/cat.py @@ -1,10 +1,23 @@ import sys +import argparse -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 +# 1. Set up argparse to handle flags and files +parser = argparse.ArgumentParser(description="A simple Python implementation of the cat command.") + +# Add the optional flags (-n and -b) +parser.add_argument("-n", action="store_true", help="Number all output lines") +parser.add_argument("-b", action="store_true", help="Number nonempty output lines, overrides -n") + +# Add the files argument (nargs="*" means it accepts 0 or more files) +parser.add_argument("files", nargs="*", help="Files to read") + +# Parse the arguments +args = parser.parse_args() + +show_all_numbers = args.n +show_non_blank_numbers = args.b +files = args.files -# 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] ") @@ -22,7 +35,7 @@ print(f"{line_count:>6}\t{line}", end="") line_count += 1 else: - # Standard cat no flags + # Standard cat no flags print(line, end="") # Logic for -n diff --git a/implement-shell-tools/ls/ls.py b/implement-shell-tools/ls/ls.py index 549d8cbbe..d3a374bec 100644 --- a/implement-shell-tools/ls/ls.py +++ b/implement-shell-tools/ls/ls.py @@ -1,14 +1,24 @@ import os import sys +import argparse -# assign flags -show_all = "-a" in sys.argv -one_column = "-1" in sys.argv +# 1. Set up argparse to handle the ls flags +parser = argparse.ArgumentParser(description="A simple Python implementation of the ls command.") -# 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 "." +# Add the -a and -1 flags +parser.add_argument("-a", action="store_true", help="Do not ignore entries starting with .") +parser.add_argument("-1", action="store_true",dest="one_column", help="List one file per line") + +# Add the path argument (nargs="?" means it's optional, default is current directory ".") +parser.add_argument("path", nargs="?", default=".", help="Directory path to list") + +# Parse the arguments +args = parser.parse_args() + +# 2. rename back variables +show_all = args.a +one_column = args.one_column +path = args.path try: # Get directory contents @@ -18,10 +28,10 @@ if show_all: entries.extend([".", ".."]) - # 5. Sort alphabetically, should used in ls.js as well + # 5. Sort alphabetically entries.sort() - # Printing Logic + # Printing Logic for entry in entries: # Skip hidden files unless -a is passed if not show_all and entry.startswith("."): diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py index 87a9445b5..2745a02f1 100644 --- a/implement-shell-tools/wc/wc.py +++ b/implement-shell-tools/wc/wc.py @@ -1,16 +1,30 @@ import sys import os +import argparse -# Flag Detection -show_lines = "-l" in sys.argv -show_words = "-w" in sys.argv -show_chars = "-c" in sys.argv +# 1. Set up argparse to handle the wc flags +parser = argparse.ArgumentParser(description="A simple Python implementation of the wc command.") + +# Add the optional flags (-l, -w, -c) +parser.add_argument("-l", action="store_true", help="Print the newline counts") +parser.add_argument("-w", action="store_true", help="Print the word counts") +parser.add_argument("-c", action="store_true", help="Print the byte counts") + +# Add the files argument (nargs="*" means it accepts 0 or more files) +parser.add_argument("files", nargs="*", help="Files to process") + +# Parse the arguments +args = parser.parse_args() + +# 2. Rename back the variables +show_lines = args.l +show_words = args.w +show_chars = args.c +files = args.files # 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