From 8c4568a36b50e8c141d54e34b0556cb391207d6e Mon Sep 17 00:00:00 2001 From: Pezhman-Azizi <80008463+Pezhman-Azizi@users.noreply.github.com> Date: Fri, 12 Dec 2025 01:06:50 +0000 Subject: [PATCH 1/3] add basic cat implementation in python --- implement-shell-tools/cat/cat.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 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 00000000..c1078ed9 --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import sys +import argparse + +# Create the argument parser – similar style to `count-containing-words` +parser = argparse.ArgumentParser( + prog="cat", + description="Prints the contents of one or more files", +) + +# positional argument: one or more paths +parser.add_argument( + "paths", + nargs="+", + help="The file(s) to print", +) + +args = parser.parse_args() + +# Loop over each path and print its content +for path in args.paths: + try: + with open(path, "r", encoding="utf-8") as f: + for line in f: + # print line exactly as it appears in the file + sys.stdout.write(line) + except FileNotFoundError: + # match shell cat-style error message + sys.stderr.write(f"cat: {path}: No such file or directory\n") From 310c2ad17e1af5f39d06473684f2ac92119d2653 Mon Sep 17 00:00:00 2001 From: Pezhman-Azizi <80008463+Pezhman-Azizi@users.noreply.github.com> Date: Fri, 12 Dec 2025 01:08:58 +0000 Subject: [PATCH 2/3] add simple ls tool (-1 and -a) in python --- implement-shell-tools/ls/ls.py | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 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 00000000..af73ad69 --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse + +parser = argparse.ArgumentParser( + prog="ls", + description="Simple reimplementation of ls", +) + +parser.add_argument( + "-1", + dest="one_per_line", + action="store_true", + help="List one entry per line", +) + +parser.add_argument( + "-a", + dest="all_files", + action="store_true", + help="Do not ignore entries starting with .", +) + +# Optional path (default = current directory) +parser.add_argument( + "path", + nargs="?", + default=".", + help="Directory to list (default: current directory)", +) + +args = parser.parse_args() + +# Get all items in the directory +try: + entries = os.listdir(args.path) +except FileNotFoundError: + sys.stderr.write(f"ls: cannot access '{args.path}': No such file or directory\n") + sys.exit(1) + +# Handle -a (hidden files) +if not args.all_files: + entries = [e for e in entries if not e.startswith(".")] + +# Sort for stable behaviour +entries.sort() + +# Output: -1 = one per line +if args.one_per_line: + for entry in entries: + print(entry) +else: + # default: space-separated + print(" ".join(entries)) From 4debef1382f54715c0c335d8fc9d87de1dbc0de3 Mon Sep 17 00:00:00 2001 From: Pezhman-Azizi <80008463+Pezhman-Azizi@users.noreply.github.com> Date: Fri, 12 Dec 2025 01:09:51 +0000 Subject: [PATCH 3/3] add wc tool with line, word, and byte counting --- implement-shell-tools/wc/wc.py | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 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 00000000..fd552a99 --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +import argparse +import sys +import os + +parser = argparse.ArgumentParser( + prog="wc", + description="Simple reimplementation of wc" +) + +parser.add_argument( + "-l", + dest="count_lines", + action="store_true", + help="Print the line counts" +) + +parser.add_argument( + "-w", + dest="count_words", + action="store_true", + help="Print the word counts" +) + +parser.add_argument( + "-c", + dest="count_bytes", + action="store_true", + help="Print the byte counts" +) + +parser.add_argument( + "paths", + nargs="+", + help="File(s) to process" +) + +args = parser.parse_args() + + +def wc_file(path): + """Return (lines, words, bytes) for a file.""" + try: + with open(path, "r", encoding="utf-8") as f: + content = f.read() + except FileNotFoundError: + sys.stderr.write(f"wc: {path}: No such file or directory\n") + return None + + lines = content.count("\n") + (1 if content and not content.endswith("\n") else 0) + words = len(content.split()) + bytes_ = len(content.encode("utf-8")) + + return lines, words, bytes_ + + +# If no flags are supplied, show all three +show_all = not (args.count_lines or args.count_words or args.count_bytes) + +totals = [0, 0, 0] # lines, words, bytes +multiple_files = len(args.paths) > 1 + +for path in args.paths: + result = wc_file(path) + if result is None: + continue + + lines, words, bytes_ = result + + totals[0] += lines + totals[1] += words + totals[2] += bytes_ + + output_parts = [] + + if show_all: + output_parts.extend([str(lines), str(words), str(bytes_)]) + else: + if args.count_lines: + output_parts.append(str(lines)) + if args.count_words: + output_parts.append(str(words)) + if args.count_bytes: + output_parts.append(str(bytes_)) + + output_parts.append(path) + print(" ".join(output_parts)) + + +# If multiple files → print total line +if multiple_files: + totals_output = [] + + if show_all: + totals_output.extend([str(totals[0]), str(totals[1]), str(totals[2])]) + else: + if args.count_lines: + totals_output.append(str(totals[0])) + if args.count_words: + totals_output.append(str(totals[1])) + if args.count_bytes: + totals_output.append(str(totals[2])) + + totals_output.append("total") + print(" ".join(totals_output))