diff --git a/.gitignore b/.gitignore index 3c3629e6..cc8b5568 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +prep/ diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py new file mode 100644 index 00000000..7743c111 --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,37 @@ +import sys +import argparse + +# Setup argument parser +parser = argparse.ArgumentParser( + prog="cat", + description="Concatenate and display files" +) + +parser.add_argument("-n", "--number", action="store_true", help="Number all output lines") +parser.add_argument("-b", "--number-nonblank", action="store_true", help="Number non-empty lines only") +parser.add_argument("paths", nargs='+', help="Files to read") + +args = parser.parse_args() + +line_number = 1 # Shared counter across all files + +for path in args.paths: + with open(path, "r") as f: + + lines = f.read().splitlines() + + output = [] + for line in lines: + if args.number_nonblank: + if line.strip(): + output.append(f"{line_number:6}\t{line}") + line_number += 1 + else: + output.append(line) + elif args.number: + output.append(f"{line_number:6}\t{line}") + line_number += 1 + else: + output.append(line) + + print("\n".join(output)) diff --git a/implement-shell-tools/ls/ls.py b/implement-shell-tools/ls/ls.py new file mode 100644 index 00000000..e1e301b2 --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,30 @@ +import os +import argparse + +parser = argparse.ArgumentParser(prog="ls", description="Lists contents of a directory") + +parser.add_argument("-1", dest="one_column", action="store_true", help="List one file per line") +parser.add_argument("-a", "--all", action="store_true", help="Include hidden files") +parser.add_argument("paths", nargs='*', default=["."], help="Directories to list") + +args = parser.parse_args() + +for path in args.paths: + if len(args.paths) > 1: + print(f"{path}:") + + try: + files = os.listdir(path) + if not args.all: + files = [f for f in files if not f.startswith('.')] + + files.sort() + + sep = "\n" if args.one_column else " " + print(sep.join(files)) + + if len(args.paths) > 1: + print() + + except FileNotFoundError: + print(f"ls: cannot access '{path}': No such file or directory") diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py new file mode 100644 index 00000000..500ac55e --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,42 @@ +import argparse + +parser = argparse.ArgumentParser(prog="wc", description="Count lines, words, and characters") +parser.add_argument("paths", nargs='+', help="Files to count") +parser.add_argument("-l", "--lines", action="store_true") +parser.add_argument("-w", "--words", action="store_true") +parser.add_argument("-c", "--chars", action="store_true") + +args = parser.parse_args() + +# If no flags are provided, the default behavior is to show all three +show_all = not (args.lines or args.words or args.chars) + +total_stats = [0, 0, 0] # lines, words, chars + +for path in args.paths: + with open(path, "r") as f: + content = f.read() + + stats = [ + len(content.splitlines()), + len(content.split()), + len(content) + ] + + for i in range(3): + total_stats[i] += stats[i] + + output = [] + if args.lines or show_all: output.append(f"{stats[0]:8}") + if args.words or show_all: output.append(f"{stats[1]:8}") + if args.chars or show_all: output.append(f"{stats[2]:8}") + + print(f"{''.join(output)} {path}") + +if len(args.paths) > 1: + total_output = [] + if args.lines or show_all: total_output.append(f"{total_stats[0]:8}") + if args.words or show_all: total_output.append(f"{total_stats[1]:8}") + if args.chars or show_all: total_output.append(f"{total_stats[2]:8}") + + print(f"{''.join(total_output)} total") \ No newline at end of file