Repository navigation
London | 26-SDC-July | Raihan Sharif | Sprint 4 | implement shell tools python #649
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
Open
RaihanSharif
wants to merge
22
commits into
CodeYourFuture:main
Choose a base branch
from
RaihanSharif:shell-tools-python
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
38c96e9
set up argparse with -n, -n flags
RaihanSharif 0591f3f
handle errors when reading file
RaihanSharif 6020eef
return exit code 1 for any failed file read
RaihanSharif 108fef7
seperate out file read and formatting
RaihanSharif 0c5aeb2
cat function to cat the formatted lines
RaihanSharif 9361dde
remove stray semicolon
RaihanSharif 3617742
set up parser
RaihanSharif b28fa81
read files/folders in a given folder
RaihanSharif 4f62b37
function to format and print a single directory content
RaihanSharif 2ad95a6
snake case function names
RaihanSharif 36c79a6
print multiple files and folders
RaihanSharif 34e629a
add very basic error handling
RaihanSharif 2d9a740
add a requirements file for argparse import
RaihanSharif ef611c6
parser setup
RaihanSharif 575f0bf
logic completed
RaihanSharif 87d8179
wc formatting matches real formatting exactly.
RaihanSharif 4995bfc
add utf-8 encoding to cat
RaihanSharif 0e5e2ee
no need to keep track of file count
RaihanSharif 1ea4d3e
correctly print error message
RaihanSharif 085a1ac
implement changes from code review
RaihanSharif d0e9187
remove dead code
RaihanSharif 9fa8069
numbering across files
RaihanSharif File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import argparse | ||
| import sys | ||
|
|
||
| parser = argparse.ArgumentParser( | ||
| prog="a simple cat implementation", | ||
| description="cat command line tool with the -n and -b flags" | ||
| ) | ||
|
|
||
| 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("paths", nargs="+", help="file path or paths", ) | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| # cat returns different error messages depending on the reason the path could be read | ||
| def read_file(path): | ||
| """Returns (content, error_message). error_message is None on success""" | ||
| try: | ||
| with open(path, "r", encoding="utf-8") as f: | ||
| return f.read(), None | ||
| except FileNotFoundError: | ||
| return None, f"cat: {path}: No such file or directory" | ||
| except IsADirectoryError: | ||
| return None, f"cat: {path}: Is a directory" | ||
| except PermissionError: | ||
| return None, f"cat: {path}: Permission denied" | ||
|
|
||
|
|
||
| # -b (number the non-empty lines) takes priority over -n (number all lines) | ||
| # if both are present | ||
| def format_lines(lines, line_num, number_all=False, number_nonempty=False): | ||
| """Returns a list of formatted output lines""" | ||
| output = [] | ||
| if number_nonempty: | ||
| for line in lines: | ||
| if line == "": | ||
| output.append("") | ||
| else: | ||
| # {line_num:6} right justied number, length of at least 6 | ||
| # {some_str:6} left justifed string, length fo at least 6 | ||
| output.append(f"{line_num:6}\t{line}") | ||
| line_num += 1 | ||
| elif number_all: | ||
| for _, line in enumerate(lines, start=1): | ||
| output.append(f"{line_num:6}\t{line}") | ||
| line_num += 1 | ||
| else: | ||
| output = lines | ||
|
|
||
| return output | ||
|
|
||
|
|
||
| def cat_file(path, line_num, number_all=False, number_nonempty=False): | ||
| """ | ||
| Calls read_file -> format_lines -> prints formatted line. | ||
| Returns True if file read successfully, else returns False | ||
|
|
||
| If failed to read file, prints error to stderr | ||
| """ | ||
| content, error = read_file(path) | ||
| if (error): | ||
| print(error, file=sys.stderr) | ||
| return -1 | ||
|
|
||
| # splitlines automatically trims trailing empty lines | ||
| lines = content.splitlines() | ||
| for line in format_lines(lines, line_num, number_all, number_nonempty): | ||
| print(line) | ||
|
|
||
| return len(lines) + line_num | ||
|
|
||
|
|
||
| def main(): | ||
| # cat exits with error code 1 if any file read fails | ||
| file_error = False | ||
|
|
||
| line_num = 1 | ||
| for path in args.paths: | ||
| result = cat_file(path, line_num, args.n, args.b) | ||
| if result < 0: | ||
| file_error = True | ||
|
|
||
| if result > 0: | ||
| line_num = result | ||
|
|
||
| # if at any point, file reading failed file error is set to True, | ||
| # and program exist with code 1 after all tasks completed | ||
| sys.exit(1 if file_error else 0) | ||
|
|
||
| # ensures that main only runs when this file/module is directly executed | ||
| # not when it is imported, for example, for automated tests | ||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import argparse | ||
| import sys | ||
| import os | ||
|
|
||
| parser = argparse.ArgumentParser( | ||
| prog="a simple version of ls", | ||
| description="ls command line tool which can accept 0 or more arguements" \ | ||
| "and take -a and -1 flags") | ||
|
|
||
| parser.add_argument("-a", action="store_true", help="show all files, including dot files") | ||
|
|
||
| # can't store as an attribute of Namespace object, because 1 is not a valid python identifier | ||
| # but can store under the name given in the dest argument. When working with this | ||
| # parser, look for "opt_one", not "1". | ||
| parser.add_argument("-1", dest="opt_one", action="store_true", help="show one file/directory name per line") | ||
|
|
||
| # takes 0 more arguments, if none are given, sets "." as default value | ||
| parser.add_argument("paths", nargs="*", help="file/directory path(s) to display", default=".") | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
|
|
||
| def get_dir_entries(path, aFlag=args.a): | ||
| # warning: listdir() prints current directory by default | ||
| entries = os.listdir(path) | ||
| entries = [".", ".."] + entries | ||
| entries.sort() | ||
|
|
||
| if (not args.a): | ||
| entries = [entry for entry in entries if not entry.startswith(".")] | ||
|
|
||
| return entries | ||
|
|
||
|
|
||
| def print_entries(entries, onePerLineFlag = args.opt_one): | ||
| if (onePerLineFlag): | ||
| for entry in entries: | ||
| print(entry) | ||
| elif (len(entries) > 0): | ||
| for i in range(len(entries)-1): | ||
| print(f"{entries[i]}\t", end="") | ||
| print(f"{entries[-1]}") | ||
|
|
||
|
|
||
| def main(): | ||
| # file and directory paths are processed separately | ||
| # file_args = [arg for arg in args.paths if os.path.isfile(arg)] | ||
| # dir_args = [arg for arg in args.paths if os.path.isdir(arg)] | ||
|
|
||
| file_args = [] | ||
| dir_args = [] | ||
| invalid_args = [] | ||
|
|
||
| # this is a simplication, it groups all errors under "invalid file" | ||
| # real ls would have different messages things like permission denied | ||
| # also bad because it makes two syscalls | ||
| for arg in args.paths: | ||
| if (os.path.isfile(arg)): | ||
| file_args.append(arg) | ||
| elif (os.path.isdir(arg)): | ||
| dir_args.append(arg) | ||
| else: | ||
| invalid_args.append(arg) | ||
|
|
||
| for arg in invalid_args: | ||
| print(f"ls: {arg}: No such file or directory", file=sys.stderr) | ||
|
|
||
| if (len(file_args) > 0): | ||
| print_entries(file_args) | ||
|
|
||
| for index, path in enumerate(dir_args, start=0): | ||
| if (len(args.paths) > 1): | ||
| if (index > 0 or len(file_args) > 0): | ||
| print("") | ||
| print(f"{path}:") | ||
| print_entries(get_dir_entries(path)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import argparse | ||
| import sys | ||
| import os | ||
|
|
||
| # TODO: decompose into functions to make it more modular and reusable | ||
|
|
||
| parser = argparse.ArgumentParser( | ||
| prog="a simple version of wc. Takes in one or more files.", | ||
| description="ls command line tool which can accept -l -w -c cflags") | ||
|
|
||
| parser.add_argument("-l", action="store_true", help="show line count") | ||
| parser.add_argument("-w", action="store_true", help="show word count") | ||
| parser.add_argument("-c", action="store_true", help="show byte count") | ||
|
|
||
| parser.add_argument("paths", nargs="*", help="file(s) for which to show data") | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| totals = {"l": 0, "w": 0, "c": 0} | ||
|
|
||
| # if no flags then set all flags to true, same as in real wc | ||
| if (not args.l and not args.w and not args.c): | ||
| args.l = args.w = args.c = True | ||
|
|
||
| for path in args.paths: | ||
| if (not os.path.exists(path)): | ||
| print(f"wc: {path}: open: No such file or directory", file=sys.stderr) | ||
| elif (os.path.isdir(path)): | ||
| print(f"wc: {path}: read: Is a directory", file=sys.stderr) | ||
| elif (os.path.isfile(path)): | ||
| output_str = "" | ||
| with open(path, "r", encoding="utf-8") as file: | ||
| content = file.read() | ||
|
|
||
| lines = content.split('\n') | ||
|
|
||
| if (args.l): | ||
| line_count = content.count('\n') | ||
| totals["l"] += line_count | ||
| output_str += f"{line_count:8}" | ||
|
|
||
| if (args.w): | ||
| word_count = len(content.split()) | ||
| totals["w"] += word_count | ||
| output_str += f"{word_count:8}" | ||
|
|
||
| if (args.c): | ||
| bytes = os.path.getsize(path) | ||
| totals["c"] += bytes | ||
| output_str += f"{bytes:8}" | ||
|
|
||
| output_str += f" {path}" | ||
| print(output_str) | ||
|
|
||
| if (len(args.paths) > 1): | ||
| res = {key : val for key, val in totals.items() | ||
| if val != 0} | ||
| total_str = "" | ||
| for v in res.values(): | ||
| total_str += f"{v:8}" | ||
|
|
||
| total_str += " total" | ||
| print(total_str) | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.