diff --git a/implement-shell-tools/.gitignore b/implement-shell-tools/.gitignore new file mode 100644 index 00000000..77ac7549 --- /dev/null +++ b/implement-shell-tools/.gitignore @@ -0,0 +1,3 @@ +.venv/ +__pycache__/ +*.pyc diff --git a/implement-shell-tools/cat/sample-files/mycat.py b/implement-shell-tools/cat/sample-files/mycat.py new file mode 100644 index 00000000..4479fbc7 --- /dev/null +++ b/implement-shell-tools/cat/sample-files/mycat.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +import sys +import os + +def print_file(filename, number_all=False, number_nonempty=False): + try: + with open(filename, 'r') as f: + lines = f.readlines() + except FileNotFoundError: + print(f"cat: {filename}: No such file") + return + + counter = 1 + for line in lines: + line = line.rstrip('\n') + if number_all: + print(f"{counter:6} {line}") + counter += 1 + elif number_nonempty: + if line.strip(): + print(f"{counter:6} {line}") + counter += 1 + else: + print(line) + else: + print(line) + +def main(): + import argparse + + parser = argparse.ArgumentParser(description="A Python implementation of cat") + parser.add_argument('files', nargs='+', help="Files to print") + parser.add_argument('-n', '--number-all', action='store_true', help="Number all lines") + parser.add_argument('-b', '--number-nonempty', action='store_true', help="Number non-empty lines") + args = parser.parse_args() + + for file in args.files: + print_file(file, number_all=args.number_all, number_nonempty=args.number_nonempty) + +if __name__ == "__main__": + main() diff --git a/implement-shell-tools/ls/sample-files/myls.py b/implement-shell-tools/ls/sample-files/myls.py new file mode 100644 index 00000000..34358d2c --- /dev/null +++ b/implement-shell-tools/ls/sample-files/myls.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import argparse +import os + +def list_directory(dir_path, show_all): + if not os.path.exists(dir_path): + print(f"ls: cannot access '{dir_path}': No such file or directory") + return + + if os.path.isfile(dir_path): + print(dir_path) + return + + try: + entries = os.listdir(dir_path) + except PermissionError: + print(f"ls: cannot access '{dir_path}': Permission denied") + return + + if not show_all: + entries = [e for e in entries if not e.startswith(".")] + else: + entries = [".", ".."] + entries + + entries.sort() + for e in entries: + print(e) + +def main(): + parser = argparse.ArgumentParser(description="Custom implementation of ls") + parser.add_argument("-1", action="store_true", help="list one file per line (default)") + parser.add_argument("-a", "--all", action="store_true", help="include hidden files") + parser.add_argument("dir", nargs="?", default=".", help="directory to list") + + args = parser.parse_args() + + list_directory(args.dir, args.all) + +if __name__ == "__main__": + main() diff --git a/implement-shell-tools/wc/sample-files/mywc.py b/implement-shell-tools/wc/sample-files/mywc.py new file mode 100644 index 00000000..4c3e1d56 --- /dev/null +++ b/implement-shell-tools/wc/sample-files/mywc.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import argparse +import glob +import os + +def count_lines(text): + if not text: + return 0 + lines = text.splitlines() + return len(lines) + +def count_words(text): + return len([w for w in text.split() if w]) + +def count_chars(text): + return len(text.encode('utf-8')) + +def wc_file(filename, options): + try: + with open(filename, "r", encoding="utf-8") as f: + text = f.read() + except FileNotFoundError: + print(f"wc: {filename}: No such file") + return None + + lines = count_lines(text) + words = count_words(text) + chars = count_chars(text) + + padding = 7 + if options.lines and not options.words and not options.chars: + output = f"{lines} {filename}" + elif options.words and not options.lines and not options.chars: + output = f"{words} {filename}" + elif options.chars and not options.lines and not options.words: + output = f"{chars} {filename}" + else: + output = f"{str(lines).rjust(padding)} {str(words).rjust(padding)} {str(chars).rjust(padding)} {filename}" + + print(output) + return {"lines": lines, "words": words, "chars": chars} + +def main(): + parser = argparse.ArgumentParser(description="Custom implementation of wc") + parser.add_argument("-l", "--lines", action="store_true", help="count lines") + parser.add_argument("-w", "--words", action="store_true", help="count words") + parser.add_argument("-c", "--chars", action="store_true", help="count characters") + parser.add_argument("files", nargs="+", help="files or wildcard patterns") + args = parser.parse_args() + + all_files = [] + for pattern in args.files: + expanded = glob.glob(pattern) + if not expanded: + print(f"wc: {pattern}: No such file or directory") + all_files.extend(expanded) + + total_lines = total_words = total_chars = 0 + + for file in all_files: + result = wc_file(file, args) + if result: + total_lines += result["lines"] + total_words += result["words"] + total_chars += result["chars"] + + padding = 7 + if len(all_files) > 1: + if args.lines and not args.words and not args.chars: + print(f"{total_lines} total") + elif args.words and not args.lines and not args.chars: + print(f"{total_words} total") + elif args.chars and not args.lines and not args.words: + print(f"{total_chars} total") + else: + print(f"{str(total_lines).rjust(padding)} {str(total_words).rjust(padding)} {str(total_chars).rjust(padding)} total") + +if __name__ == "__main__": + main()