diff --git a/implement-shell-tools/cat/README.md b/implement-shell-tools/cat/README.md index 7284a5e6..86291b40 100644 --- a/implement-shell-tools/cat/README.md +++ b/implement-shell-tools/cat/README.md @@ -1,4 +1,4 @@ -# Implement `cat` +# Implement `cat` in python You should already be familiar with the `cat` command line tool. @@ -15,3 +15,20 @@ It must act the same as `cat` would, if run from the directory containing this R Matching any additional behaviours or flags are optional stretch goals. We recommend you start off supporting no flags, then add support for `-n`, then add support for `-b`. + +*========commands for running the scripts======== + +* No flags +python cat.py sample-files/1.txt + +* Number every line (-n) +python cat.py -n sample-files/1.txt + +* Display all files in directory(shell expands *.txt) +python cat.py sample-files/*.txt + +* Number every line across multiple files(including empty ones) +python cat.py -n sample-files/*.txt + +* Number non-empty lines only (-b) +python cat.py -b sample-files/3.txt diff --git a/implement-shell-tools/cat/cat.py b/implement-shell-tools/cat/cat.py new file mode 100644 index 00000000..96bbcdbc --- /dev/null +++ b/implement-shell-tools/cat/cat.py @@ -0,0 +1,50 @@ +import argparse +# ------------------------------------------- +# Set up the argument parser +# ------------------------------------------- + +parser = argparse.ArgumentParser( + prog ="display-file-content", + description = "Implement cat command with -n and -b flag support", + ) + +parser.add_argument("-n", "--number-all-lines", + action="store_true", + help="Number every line in the file" + ) + +parser.add_argument("-b", "--number-non-empty-lines", + action="store_true", + help="Number non empty lines in the file" + ) + +parser.add_argument("paths", nargs="+", help="File paths to process") + +args = parser.parse_args() + +# ------------------------------------------- +# Implement functionality +# ------------------------------------------- + +line_number = 1 + +for filepath in args.paths: + with open(filepath, "r", encoding="utf-8") as f: + content = f.read() + + lines = content.split("\n") + + for line in lines: + if args.number_all_lines: + print(f"{line_number} {line}") + line_number += 1 + + elif args.number_non_empty_lines: + if line.strip() == "": + print(line) + else: + print(f"{line_number} {line}") + line_number +=1 + + else: + print(line) diff --git a/implement-shell-tools/ls/README.md b/implement-shell-tools/ls/README.md index edbfb811..2190f603 100644 --- a/implement-shell-tools/ls/README.md +++ b/implement-shell-tools/ls/README.md @@ -13,3 +13,8 @@ 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`. + +*=======command for running the script=========== +python3 ls_py.py -1 +python3 ls_py.py -1 sample-files +python3 ls_py.py -1 -a sample-files diff --git a/implement-shell-tools/ls/ls_py.py b/implement-shell-tools/ls/ls_py.py new file mode 100755 index 00000000..82df9640 --- /dev/null +++ b/implement-shell-tools/ls/ls_py.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys + + +# Set up the argument parser +def parse_args(): + parser = argparse.ArgumentParser( + prog="list_files_in_directory", + description="Implement ls commands to list files in directory" + ) + + parser.add_argument( + "-1","--file-per-line", + action="store_true", + dest="file_per_line", + help="list files one per line" + ) + + parser.add_argument( + "-a", + "--files-and-hidden-ones", + action="store_true", + dest="files_and_hidden_ones", + help="list all files including hidden ones", + ) + + parser.add_argument( + "paths", + nargs="*", + help="directories to list" + ) + + return parser.parse_args() + +# if no paths, default to current directory +def get_paths(args): + if len(args.paths) == 0: + return ["."] + return args.paths + +# list a single directory +def list_directory(directory_path, show_hidden, file_per_line): + try: + entries = os.listdir(directory_path) + except OSError as err: + print(f"ls: cannot access '{directory_path}': {err}", file=sys.stderr) + return + + if not show_hidden: + entries = [name for name in entries if not name.startswith(".")] + + if file_per_line: + for name in entries: + print(name) + else: + print(" ".join(entries)) + +def main(): + args = parse_args() + paths = get_paths(args) + + for directory_path in paths: + list_directory( + directory_path=directory_path, + show_hidden=args.files_and_hidden_ones, + file_per_line=args.file_per_line, + ) + +if __name__ == "__main__": + main() diff --git a/implement-shell-tools/wc/README.md b/implement-shell-tools/wc/README.md index bd76b655..7261aca7 100644 --- a/implement-shell-tools/wc/README.md +++ b/implement-shell-tools/wc/README.md @@ -1,4 +1,4 @@ -# Implement `wc` +# Implement `wc` in python You should already be familiar with the `wc` command line tool. @@ -15,3 +15,20 @@ 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. + +*======Command for testing the script======== + +* All sample files +python wc.py sample-files/* + +* Just lines +python wc.py -l sample-files/3.txt + +* Just words +python wc.py -w sample-files/3.txt + +* Just characters +python wc.py -c sample-files/3.txt + +* Lines with multiple files (to see totals) +python wc.py -l sample-files/* diff --git a/implement-shell-tools/wc/wc.py b/implement-shell-tools/wc/wc.py new file mode 100644 index 00000000..2420f5d3 --- /dev/null +++ b/implement-shell-tools/wc/wc.py @@ -0,0 +1,93 @@ +import argparse +import sys +import re + + +def main(): + # -------------------------------------------------------- + # Set up argparse + # -------------------------------------------------------- + parser= argparse.ArgumentParser( + prog="wc", + description ="wc command implementation in python" + ) + + parser.add_argument("-l", action="store_true", help ="show number of lines only") + parser.add_argument("-w", action="store_true", help="show number of words only") + parser.add_argument("-c", action="store_true", help="show number of characters only") + + parser.add_argument("paths", nargs="*", help="file paths to process") + + args = parser.parse_args() + + # -------------------------------------------------------- + # Ensures at least one path exists + # -------------------------------------------------------- + if len(args.paths) == 0: + print("wc: no file specified", file=sys.stderr) + sys.exit(1) + + totals= {"lines": 0, "words": 0, "chars": 0} + + # -------------------------------------------------------- + # Loop over each file path and process it + # -------------------------------------------------------- + for file_path in args.paths: + try: + with open(file_path, "r", encoding="utf-8") as f: + content = f.read() + except OSError as err: + print(f"wc: cannot read file'{file_path}': {err}", file=sys.stderr) + continue + + # -------------------------------------------------------- + # Count values + # -------------------------------------------------------- + line_count = len(content.split("\n")) + + words = [w for w in re.split(r"\s+", content) if w] + word_count = len(words) + + char_count = len(content) + + totals["lines"] += line_count + totals["words"] += word_count + totals["chars"] +=char_count + + # -------------------------------------------------------- + # Decide what to print based on flags + # -------------------------------------------------------- + no_flags = not args.l and not args.w and not args.c + + if no_flags: + print(f"{line_count} {word_count} {char_count} {file_path}") + continue + + if args.l: + print(f"{line_count} {file_path}" ) + + if args.w: + print(f"{word_count} {file_path}") + + if args.c: + print(f"{char_count} {file_path}") + + + # -------------------------------------------------------- + # Print totals if there are multiple files + # -------------------------------------------------------- + if len(args.paths) > 1: + no_flags = not args.l and not args.w and not args.c + + if no_flags: + print(f"{totals['lines']} {totals['words']} {totals['chars']} total") + + if args.l: + print(f"{totals['lines']} total") + if args.w: + print(f"{totals['words']} total") + if args.c: + print(f"{totals['chars']} total") + +if __name__ == "__main__": + main()