-
-
Notifications
You must be signed in to change notification settings - Fork 90
West Midlands | 26 March SDC | Iswat Bello | Sprint 4 | implement shell tools in python #505
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
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 |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| node_modules | ||
| .venv | ||
| *.class |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import sys | ||
| import argparse | ||
|
|
||
| def read_and_output_files(): | ||
| # 1. Setup Argument Parser (Equivalent to 'commander') | ||
| parser = argparse.ArgumentParser(description="Python implementation of a basic cat-like utility") | ||
| parser.add_argument("-n", "--number", action="store_true", help="number all output lines") | ||
| parser.add_argument("-b", "--number-nonblank", action="store_true", help="number only non-empty lines") | ||
| parser.add_argument("files", nargs="+", help="files to read") | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| try: | ||
| # 2. Read all file contents (Equivalent to Promise.all / fs.readFile) | ||
| file_contents = [] | ||
| for file_path in args.files: | ||
| with open(file_path, "r", encoding="utf-8") as f: | ||
| file_contents.append(f.read()) | ||
|
|
||
| concatenated_content = "".join(file_contents) | ||
|
|
||
| # 3. Process Logic | ||
| if args.number: | ||
| # -n logic: number all lines | ||
| lines = concatenated_content.split("\n") | ||
| output = [] | ||
| for index, line in enumerate(lines, start=1): | ||
| # rjust(6) is equivalent to padStart(6) | ||
| output.append(f"{str(index).rjust(6)} {line}") | ||
| sys.stdout.write("\n".join(output)) | ||
|
|
||
| elif args.number_nonblank: | ||
| # -b logic: number only non-empty lines | ||
| lines = concatenated_content.split("\n") | ||
| output = [] | ||
| nonblank_line_number = 0 | ||
| for line in lines: | ||
| if line.strip() == "": | ||
| output.append(line) | ||
| else: | ||
| nonblank_line_number += 1 | ||
| output.append(f"{str(nonblank_line_number).rjust(6)} {line}") | ||
| sys.stdout.write("\n".join(output)) | ||
|
|
||
| else: | ||
| # No flags: standard output | ||
| sys.stdout.write(concatenated_content) | ||
|
|
||
| except Exception as err: | ||
| print(f"Error reading multiple files: {err}", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
| if __name__ == "__main__": | ||
| read_and_output_files() |
|
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. The implementation looks good to me. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import os | ||
| import sys | ||
| import argparse | ||
|
|
||
| def run_ls_command(): | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument("-1", "--one-per-line", action="store_true", dest="one") | ||
| parser.add_argument("-a", "--all", action="store_true") | ||
| parser.add_argument("path", nargs="?", default=".") | ||
| args = parser.parse_args() | ||
|
|
||
| # ANSI Color Codes | ||
| BLUE = '\033[34m' | ||
| RESET = '\033[0m' | ||
|
|
||
| try: | ||
| # 1. Get and sort entries | ||
| directory_entries = os.listdir(args.path) | ||
| directory_entries.sort(key=str.lower) | ||
|
|
||
| # 2. Filter out dotfiles unless -a is used | ||
| visible_entries = [] | ||
| if args.all: | ||
| visible_entries = directory_entries | ||
| else: | ||
| for name in directory_entries: | ||
| if not name.startswith("."): | ||
| visible_entries.append(name) | ||
|
|
||
| # 3. Apply colors to folders | ||
| colored_entries = [] | ||
| for name in visible_entries: | ||
| # We must join the path to the name to check if it's a folder correctly | ||
| full_path = os.path.join(args.path, name) | ||
|
|
||
| if os.path.isdir(full_path): | ||
| # Wrap the name in Blue color codes | ||
| colored_entries.append(f"{BLUE}{name}{RESET}") | ||
| else: | ||
| # Keep regular file name as is | ||
| colored_entries.append(name) | ||
|
|
||
| # 4. Build output string | ||
| output_string = "" | ||
| if args.one: | ||
| output_string = "\n".join(colored_entries) + "\n" | ||
| else: | ||
| output_string = " ".join(colored_entries) + "\n" | ||
|
|
||
| if colored_entries: | ||
| sys.stdout.write(output_string) | ||
|
|
||
| except Exception as err: | ||
| print(f"Error: {err}", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
| if __name__ == "__main__": | ||
| run_ls_command() |
|
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. Suggestion: The |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import argparse | ||
| import sys | ||
| import os | ||
|
|
||
|
|
||
| def calculate_stats(content, display_name, original_bytes=None): | ||
| lines = content.count("\n") | ||
| words = len(content.split()) | ||
| # If we have the raw bytes, use that length. Otherwise, encode to get byte length. | ||
| byte_count = ( | ||
| original_bytes if original_bytes is not None else len(content.encode("utf-8")) | ||
| ) | ||
|
|
||
| return { | ||
| "lineCount": lines, | ||
| "wordCount": words, | ||
| "byteCount": byte_count, | ||
| "displayName": display_name, | ||
| } | ||
|
|
||
|
|
||
| def print_formatted_report(stats, args, should_show_all_stats): | ||
| output_columns = [] | ||
|
|
||
| def format_col(count): | ||
| return str(count).rjust(4) | ||
|
|
||
| if should_show_all_stats: | ||
| output_columns.append(format_col(stats["lineCount"])) | ||
| output_columns.append(format_col(stats["wordCount"])) | ||
| output_columns.append(format_col(stats["byteCount"])) | ||
| else: | ||
| if args.lines: | ||
| output_columns.append(format_col(stats["lineCount"])) | ||
| if args.words: | ||
| output_columns.append(format_col(stats["wordCount"])) | ||
| if args.bytes: | ||
| output_columns.append(format_col(stats["byteCount"])) | ||
|
|
||
| # Use a single space between the numbers and the name | ||
| print(f"{''.join(output_columns)} {stats['displayName']}") | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description="A simple Python implementation of wc") | ||
| parser.add_argument("files", nargs="*", help="Files to process") | ||
| parser.add_argument( | ||
| "-l", "--lines", action="store_true", help="print the newline counts" | ||
| ) | ||
| parser.add_argument( | ||
| "-w", "--words", action="store_true", help="print the word counts" | ||
| ) | ||
| parser.add_argument( | ||
| "-c", "--bytes", action="store_true", help="print the byte counts" | ||
| ) | ||
|
|
||
| args = parser.parse_args() | ||
| should_show_all_stats = not (args.lines or args.words or args.bytes) | ||
| all_file_stats = [] | ||
| exit_code = 0 | ||
|
|
||
| # NEW: Handle Standard Input if no files are provided | ||
| if not args.files: | ||
| stdin_content = sys.stdin.read() | ||
| stats = calculate_stats(stdin_content, "") | ||
| print_formatted_report(stats, args, should_show_all_stats) | ||
| return | ||
|
|
||
| # Process files | ||
| for file_path in args.files: | ||
| try: | ||
| with open(file_path, "rb") as f: | ||
| raw_bytes = f.read() | ||
| content = raw_bytes.decode("utf-8", errors="ignore") | ||
| stats = calculate_stats(content, file_path, len(raw_bytes)) | ||
|
|
||
| all_file_stats.append(stats) | ||
| print_formatted_report(stats, args, should_show_all_stats) | ||
| except Exception: | ||
| print(f"wc: {file_path}: No such file or directory", file=sys.stderr) | ||
| exit_code = 1 | ||
|
|
||
| # Print total if more than one file | ||
| if len(all_file_stats) > 1: | ||
| grand_totals = { | ||
| "lineCount": sum(s["lineCount"] for s in all_file_stats), | ||
| "wordCount": sum(s["wordCount"] for s in all_file_stats), | ||
| "byteCount": sum(s["byteCount"] for s in all_file_stats), | ||
| "displayName": "total", | ||
| } | ||
| print_formatted_report(grand_totals, args, should_show_all_stats) | ||
|
|
||
| if exit_code != 0: | ||
| sys.exit(exit_code) | ||
|
|
||
|
|
||
| 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.
"\n".join(output)omits the final newline, it is different from the behavour of the realcat. Please look at the screenshot.line.strip() == ""is too agressive, it treats lines containing only space or tabs as blank, whil the realcat -bonly skips truly empty lines.