diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py new file mode 100644 index 00000000..608b21d9 --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import sys +import glob + +def cat(files, number_all=False, number_nonblank=False): + line_num = 1 + for filename in files: + try: + with open(filename, "r") as f: + for line in f: + stripped = line.rstrip("\n") + + if number_all: + print(f"{line_num:6}\t{stripped}") + line_num += 1 + elif number_nonblank: + if stripped: + print(f"{line_num:6}\t{stripped}") + line_num += 1 + else: + print() + else: + print(stripped) + except FileNotFoundError: + print(f"cat: {filename}: No such file or directory", file=sys.stderr) + +def main(): + args = sys.argv[1:] + + number_all = "-n" in args + number_nonblank = "-b" in args + + # Remove flags from args + files = [arg for arg in args if arg not in ("-n", "-b")] + + # Expand globs like *.txt + expanded_files = [] + for file in files: + expanded_files.extend(glob.glob(file)) + + if not expanded_files: + print("cat: missing file operand", file=sys.stderr) + sys.exit(1) + + cat(expanded_files, number_all=number_all, number_nonblank=number_nonblank) + +if __name__ == "__main__": + main() diff --git a/implement-shell-tools/ls/ls.py b/implement-shell-tools/ls/ls.py new file mode 100644 index 00000000..21f50ed3 --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import sys +import os + +def list_dir(path=".", show_all=False): + try: + entries = os.listdir(path) + if not show_all: + entries = [e for e in entries if not e.startswith(".")] + return sorted(entries) + except FileNotFoundError: + print(f"ls: cannot access '{path}': No such file or directory", file=sys.stderr) + return [] + except NotADirectoryError: + # If it's a file, just return it + return [path] + +def main(): + args = sys.argv[1:] + + # Flags + show_one_per_line = "-1" in args + show_all = "-a" in args + + # Remove flags from args + paths = [arg for arg in args if arg not in ("-1", "-a")] + + if not paths: + paths = ["."] # default to current directory + + for i, path in enumerate(paths): + entries = list_dir(path, show_all=show_all) + + if len(paths) > 1: + print(f"{path}:") + + for entry in entries: + if show_one_per_line: + print(entry) + else: + print(entry, end=" ") + if not show_one_per_line: + print() # newline after the row + + if i < len(paths) - 1: + print() + +if __name__ == "__main__": + main() diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py new file mode 100644 index 00000000..a6a90e8f --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import sys +import glob +import os + +def count_file(filename, count_lines, count_words, count_bytes): + try: + with open(filename, "rb") as f: # open in binary to count bytes correctly + data = f.read() + except FileNotFoundError: + print(f"wc: {filename}: No such file or directory", file=sys.stderr) + return None + + text = data.decode(errors="ignore") + lines = text.splitlines() + + l = len(lines) + w = len(text.split()) + c = len(data) + + results = [] + if count_lines: + results.append(str(l)) + if count_words: + results.append(str(w)) + if count_bytes: + results.append(str(c)) + + if not (count_lines or count_words or count_bytes): + # default: all three + results = [str(l), str(w), str(c)] + + return results, filename, (l, w, c) + +def main(): + args = sys.argv[1:] + + count_lines = "-l" in args + count_words = "-w" in args + count_bytes = "-c" in args + + # remove flags from args + files = [arg for arg in args if arg not in ("-l", "-w", "-c")] + + if not files: + print("wc: missing file operand", file=sys.stderr) + sys.exit(1) + + expanded_files = [] + for f in files: + expanded_files.extend(glob.glob(f)) + + if not expanded_files: + print("wc: no matching files", file=sys.stderr) + sys.exit(1) + + total_l, total_w, total_c = 0, 0, 0 + + for filename in expanded_files: + result = count_file(filename, count_lines, count_words, count_bytes) + if result: + res, name, (l, w, c) = result + print(f"{' '.join(res)} {name}") + total_l += l + total_w += w + total_c += c + + if len(expanded_files) > 1: + total_results = [] + if count_lines: + total_results.append(str(total_l)) + if count_words: + total_results.append(str(total_w)) + if count_bytes: + total_results.append(str(total_c)) + if not (count_lines or count_words or count_bytes): + total_results = [str(total_l), str(total_w), str(total_c)] + print(f"{' '.join(total_results)} total") + +if __name__ == "__main__": + main()