diff --git a/implement-shell-tools/ls/README.md b/implement-shell-tools/ls/README.md index edbfb81..8b41add 100644 --- a/implement-shell-tools/ls/README.md +++ b/implement-shell-tools/ls/README.md @@ -13,3 +13,6 @@ It must act the same as `ls` would, if run from the directory containing this RE Matching any additional behaviours or flags are optional stretch goals. We recommend you start off supporting just `-1`, then adding support for `-a`. + +Run the Python implementation from this directory with `python ls.py -1 sample-files` +or `python ls.py -1 -a sample-files`. Python 3 is required. diff --git a/implement-shell-tools/ls/ls.py b/implement-shell-tools/ls/ls.py new file mode 100644 index 0000000..4ef928c --- /dev/null +++ b/implement-shell-tools/ls/ls.py @@ -0,0 +1,27 @@ +"""List directory entries, one per line.""" + +import argparse +from pathlib import Path +import sys + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("-1", action="store_true", help="print one entry per line") + parser.add_argument("-a", "--all", action="store_true", help="include hidden entries") + parser.add_argument("directory", nargs="?", default=".") + args = parser.parse_args() + + try: + entries = sorted(Path(args.directory).iterdir(), key=lambda entry: entry.name) + for entry in entries: + if args.all or not entry.name.startswith("."): + print(entry.name) + except OSError as error: + print(f"ls: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/implement-shell-tools/wc/README.md b/implement-shell-tools/wc/README.md index bd76b65..44eba5a 100644 --- a/implement-shell-tools/wc/README.md +++ b/implement-shell-tools/wc/README.md @@ -15,3 +15,8 @@ It must act the same as `wc` would, if run from the directory containing this RE Matching any additional behaviours or flags are optional stretch goals. We recommend you start off supporting no flags for one file, then add support for multiple files, then add support for the flags. + +Run the Python implementation from this directory with `python wc.py sample-files/1.txt` +or `python wc.py -l sample-files/3.txt`. Multiple filenames and combined count +flags are supported. Expand wildcards in your shell, or pass filenames explicitly +if your shell does not expand them. Python 3 is required. diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py new file mode 100644 index 0000000..3153db8 --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,47 @@ +"""Count lines, words, and bytes in files.""" + +import argparse +from pathlib import Path +import sys + + +def count_file(filename): + text = Path(filename).read_bytes().decode("utf-8") + return len(text.splitlines()), len(text.split()), len(text) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("-l", action="store_true", help="print line counts") + parser.add_argument("-w", action="store_true", help="print word counts") + parser.add_argument("-c", action="store_true", help="print byte counts") + parser.add_argument("files", nargs="+") + args = parser.parse_args() + selected = [args.l, args.w, args.c] + if not any(selected): + selected = [True, True, True] + + def print_counts(counts, label): + values = [str(value) for value, enabled in zip(counts, selected) if enabled] + print(" ".join([*values, label])) + + totals = [0, 0, 0] + successful = 0 + status = 0 + for filename in args.files: + try: + counts = count_file(filename) + except (OSError, UnicodeError) as error: + print(f"wc: {filename}: {error}", file=sys.stderr) + status = 1 + continue + print_counts(counts, filename) + totals = [total + value for total, value in zip(totals, counts)] + successful += 1 + if successful > 1: + print_counts(totals, "total") + return status + + +if __name__ == "__main__": + sys.exit(main())