-
-
Notifications
You must be signed in to change notification settings - Fork 42
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
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,52 @@ | ||
| import argparse | ||
| 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='') | ||
| return line_number + 1 | ||
|
|
||
| def cat(filepath, numbering, start_line): | ||
| line_number = start_line | ||
| try: | ||
| with open(filepath) as f: | ||
| for line in f: | ||
| if numbering == Numbering.NONEMPTY: | ||
| if line.strip(): | ||
| line_number = print_numbered_line(line, line_number) | ||
| else: | ||
| print(line, end='') | ||
| elif numbering == Numbering.ALL: | ||
| line_number = print_numbered_line(line, line_number) | ||
| else: | ||
| print(line, end='') | ||
| except FileNotFoundError: | ||
| print(f"cat: {filepath}: No such file or directory") | ||
| return line_number | ||
|
|
||
| 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. How did you test this implementation? I created two files and tried using
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. This comment still stands - I get different results between your program and the builtin |
||
| 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 # start line numbering | ||
| for file in args.files: | ||
| line_number = cat(file, numbering=numbering, start_line=line_number) | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import os | ||
| import argparse | ||
|
|
||
| def ls(path, one_column, show_hidden): | ||
| """List files in a directory, optionally in one column or including hidden files.""" | ||
| try: | ||
| files = os.listdir(path) | ||
| if not show_hidden: | ||
| files = [f for f in files if not f.startswith('.')] | ||
| files.sort() | ||
|
|
||
| if one_column: | ||
| print(*files, sep='\n') | ||
| else: | ||
| print(*files) | ||
| except FileNotFoundError: | ||
| print(f"ls: cannot access '{path}': No such file or directory") | ||
| except NotADirectoryError: | ||
| print(f"ls: cannot access '{path}': Not a directory") | ||
|
Comment on lines
+17
to
+19
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 stdout vs stderr
Comment on lines
+17
to
+19
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. Stretch goal: What happens if you run |
||
|
|
||
| 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() | ||
| 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() | ||
|
|
||
| ls(args.path, args.one_column, args.a) | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import argparse | ||
|
|
||
| def wc(path, count_lines, count_words, count_bytes): | ||
| """Count lines, words, and bytes for a single file.""" | ||
| try: | ||
| with open(path, 'r') as f: | ||
| content = f.read() | ||
| lines = content.splitlines() | ||
| words = content.split() | ||
| bytes_ = len(content.encode('utf-8')) | ||
|
|
||
| # Determine what to show | ||
| if not any([count_lines, count_words, count_bytes]): | ||
| count_lines = count_words = count_bytes = True | ||
|
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. Again, I'd generally avoid putting this all on one line |
||
|
|
||
| parts = [] | ||
| if count_lines: parts.append(str(len(lines))) | ||
| if count_words: parts.append(str(len(words))) | ||
| if count_bytes: parts.append(str(bytes_)) | ||
|
Comment on lines
+17
to
+19
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. It's a little unusual that two of these have |
||
|
|
||
| print(' '.join(parts), path) | ||
|
|
||
| return (len(lines) if count_lines else 0, | ||
|
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. It's interesting that you're doing this filtering twice - you're checking whether we should output lines both inside this function and when printing the totals. Personally, I would probably just always return the values here, and let the caller decide whether to show them. |
||
| len(words) if count_words else 0, | ||
| bytes_ if count_bytes else 0) | ||
| except FileNotFoundError: | ||
| print(f"wc: {path}: No such file or directory") | ||
| return (0, 0, 0) | ||
| except IsADirectoryError: | ||
| print(f"wc: {path}: Is a directory") | ||
| return (0, 0, 0) | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument('-l', action='store_true', help='Count lines') | ||
| parser.add_argument('-w', action='store_true', help='Count words') | ||
| parser.add_argument('-c', action='store_true', help='Count bytes') | ||
| parser.add_argument('paths', nargs='+', help='Files to count') | ||
| args = parser.parse_args() | ||
|
|
||
| total_lines = total_words = total_bytes = 0 | ||
|
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. It's generally pretty unusual to initialise multiple variables like this in one go, because it makes it harder to skim the code to see where a particular variable was set/initialised than initialising each on its own line. |
||
| multiple_files = len(args.paths) > 1 | ||
|
|
||
| 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 |
||
| l, w, b = wc(path, args.l, args.w, args.c) | ||
| total_lines += l | ||
| total_words += w | ||
| total_bytes += b | ||
|
|
||
| if multiple_files: | ||
| parts = [] | ||
| if args.l or not any([args.l, args.w, args.c]): parts.append(str(total_lines)) | ||
|
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. You're repeating this |
||
| if args.w or not any([args.l, args.w, args.c]): parts.append(str(total_words)) | ||
| if args.c or not any([args.l, args.w, args.c]): parts.append(str(total_bytes)) | ||
| print(' '.join(parts), 'total') | ||
|
|
||
| if __name__ == "__main__": | ||
| 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.
Does this print to stdout or stderr? Which should it print to? Why?