-
-
Notifications
You must be signed in to change notification settings - Fork 109
ZA | 25-SDC-July | Luke Manyamazi | Sprint 4 | Python Implement Shell Tools Exercises #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a31fbf
d443669
1900a28
ff86f10
fcac82c
5476f45
84dcfb7
d72fc0a
575b3f9
a658d30
d6641a7
3fc838d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import argparse | ||
| import sys | ||
| from enum import Enum | ||
|
|
||
|
|
||
| class Numbering(Enum): | ||
| NONE = 0 | ||
| ALL = 1 | ||
| NONEMPTY = 2 | ||
|
|
||
|
|
||
| def print_numbered_line(line, line_number, pad=6): | ||
| print(f"{line_number:{pad}}\t{line}", end="") | ||
|
|
||
|
|
||
| def cat(filepath, numbering, start_line): | ||
| line_number = start_line | ||
|
|
||
| try: | ||
| with open(filepath) as file: | ||
| for line in file: | ||
| should_number = ( | ||
| numbering == Numbering.ALL | ||
| or ( | ||
| numbering == Numbering.NONEMPTY | ||
| and line.strip("\n") | ||
| ) | ||
| ) | ||
|
|
||
| if should_number: | ||
| print_numbered_line(line, line_number) | ||
| line_number += 1 | ||
| else: | ||
| print(line, end="") | ||
|
|
||
| except FileNotFoundError: | ||
| print( | ||
| f"cat: {filepath}: No such file or directory", | ||
| file=sys.stderr, | ||
| ) | ||
| return line_number, False | ||
|
|
||
| except IsADirectoryError: | ||
| print( | ||
| f"cat: {filepath}: Is a directory", | ||
| file=sys.stderr, | ||
| ) | ||
| return line_number, False | ||
|
|
||
| except PermissionError: | ||
| print( | ||
| f"cat: {filepath}: Permission denied", | ||
| file=sys.stderr, | ||
| ) | ||
| return line_number, False | ||
|
|
||
| return line_number, True | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser( | ||
| description="Concatenate files and print on the standard output." | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-n", | ||
| action="store_true", | ||
| help="number all output lines", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-b", | ||
| action="store_true", | ||
| help="number non-empty output lines", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "files", | ||
| nargs="+", | ||
| help="files to concatenate", | ||
| ) | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| if args.n and args.b: | ||
| parser.error("options -n and -b are mutually exclusive") | ||
| elif args.n: | ||
| numbering = Numbering.ALL | ||
| elif args.b: | ||
| numbering = Numbering.NONEMPTY | ||
| else: | ||
| numbering = Numbering.NONE | ||
|
|
||
| line_number = 1 | ||
| success = True | ||
|
|
||
| for filepath in args.files: | ||
| line_number, file_success = cat( | ||
| filepath, | ||
| numbering=numbering, | ||
| start_line=line_number, | ||
| ) | ||
| success = success and file_success | ||
|
|
||
| return 0 if success else 1 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import argparse | ||
| import os | ||
| import sys | ||
|
|
||
|
|
||
| def ls(path, one_column, show_hidden): | ||
| try: | ||
| if os.path.isfile(path): | ||
| print(os.path.basename(path)) | ||
| return True | ||
|
|
||
| files = os.listdir(path) | ||
|
|
||
| if show_hidden: | ||
| files = [".", ".."] + files | ||
| else: | ||
| files = [file for file in files if not file.startswith(".")] | ||
|
|
||
| files.sort() | ||
|
|
||
| separator = "\n" if one_column else "\t" | ||
| print(*files, sep=separator) | ||
|
|
||
| return True | ||
|
|
||
| except FileNotFoundError: | ||
| print( | ||
| f"ls: cannot access '{path}': No such file or directory", | ||
| file=sys.stderr, | ||
| ) | ||
| except NotADirectoryError: | ||
| print( | ||
| f"ls: cannot access '{path}': Not a directory", | ||
| file=sys.stderr, | ||
| ) | ||
| except PermissionError: | ||
| print( | ||
| f"ls: cannot open directory '{path}': Permission denied", | ||
| file=sys.stderr, | ||
| ) | ||
|
|
||
| return False | ||
|
|
||
|
|
||
| def main(): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question about testing here - If I run |
||
| parser = argparse.ArgumentParser( | ||
| description="List directory contents." | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-1", | ||
| dest="one_column", | ||
| action="store_true", | ||
| help="list one file per line", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-a", | ||
| action="store_true", | ||
| help="show hidden files", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "path", | ||
| nargs="?", | ||
| default=".", | ||
| help="directory to list", | ||
| ) | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| success = ls( | ||
| path=args.path, | ||
| one_column=args.one_column, | ||
| show_hidden=args.a, | ||
| ) | ||
|
|
||
| return 0 if success else 1 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| import argparse | ||
| import sys | ||
|
|
||
|
|
||
| def wc(path): | ||
| try: | ||
| with open(path, "rb") as file: | ||
| content = file.read() | ||
|
|
||
| line_count = content.count(b"\n") | ||
| word_count = len(content.split()) | ||
| byte_count = len(content) | ||
|
|
||
| return line_count, word_count, byte_count | ||
|
|
||
| except FileNotFoundError: | ||
| print( | ||
| f"wc: {path}: No such file or directory", | ||
| file=sys.stderr, | ||
| ) | ||
| except IsADirectoryError: | ||
| print( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this happens, what will the exit code of the process be? What should it be? (Same question for |
||
| f"wc: {path}: Is a directory", | ||
| file=sys.stderr, | ||
| ) | ||
| except PermissionError: | ||
| print( | ||
| f"wc: {path}: Permission denied", | ||
| file=sys.stderr, | ||
| ) | ||
| except OSError as error: | ||
| print( | ||
| f"wc: {path}: {error}", | ||
| file=sys.stderr, | ||
| ) | ||
|
|
||
| return None | ||
|
|
||
|
|
||
| def print_stats( | ||
| line_count, | ||
| word_count, | ||
| byte_count, | ||
| filename, | ||
| show_lines, | ||
| show_words, | ||
| show_bytes, | ||
| ): | ||
| parts = [] | ||
|
|
||
| if show_lines: | ||
| parts.append(f"{line_count:7d}") | ||
|
|
||
| if show_words: | ||
| parts.append(f"{word_count:7d}") | ||
|
|
||
| if show_bytes: | ||
| parts.append(f"{byte_count:7d}") | ||
|
|
||
| print("".join(parts), filename) | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser( | ||
| description="Print newline, word, and byte counts for files." | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-l", | ||
| action="store_true", | ||
| help="print the newline count", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-w", | ||
| action="store_true", | ||
| help="print the word count", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-c", | ||
| action="store_true", | ||
| help="print the byte count", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "paths", | ||
| nargs="+", | ||
| help="files to count", | ||
| ) | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| # If no options are supplied, wc prints all three counts. | ||
| show_lines = args.l | ||
| show_words = args.w | ||
| show_bytes = args.c | ||
|
|
||
| if not any((show_lines, show_words, show_bytes)): | ||
| show_lines = True | ||
| show_words = True | ||
| show_bytes = True | ||
|
|
||
| total_lines = 0 | ||
| total_words = 0 | ||
| total_bytes = 0 | ||
| successful_files = 0 | ||
|
|
||
| for path in args.paths: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I pass multiple files, the real |
||
| counts = wc(path) | ||
|
|
||
| if counts is None: | ||
| continue | ||
|
|
||
| line_count, word_count, byte_count = counts | ||
|
|
||
| total_lines += line_count | ||
| total_words += word_count | ||
| total_bytes += byte_count | ||
| successful_files += 1 | ||
|
|
||
| print_stats( | ||
| line_count, | ||
| word_count, | ||
| byte_count, | ||
| path, | ||
| show_lines, | ||
| show_words, | ||
| show_bytes, | ||
| ) | ||
|
|
||
| if len(args.paths) > 1 and successful_files > 0: | ||
| print_stats( | ||
| total_lines, | ||
| total_words, | ||
| total_bytes, | ||
| "total", | ||
| show_lines, | ||
| show_words, | ||
| show_bytes, | ||
| ) | ||
|
|
||
| return 0 if successful_files == len(args.paths) else 1 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you test this implementation?
I created two files and tried using
cat -n /file/1 /file/2andcat -b /file/1 /file/2and compared the output with using your script, and didn't always get the same resultsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment still stands - I get different results between your program and the builtin
cat.