Skip to content

London | Ameneh Keshavarz | Implement-shell-tools| WEEK 4 #63

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
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
venv/
57 changes: 57 additions & 0 deletions implement-shell-tools/cat/cat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import argparse
import sys

def setup_arguments():
parser = argparse.ArgumentParser(
prog="cat",
description="Concatenate and display files, optionally numbering lines."
)

group = parser.add_mutually_exclusive_group()
group.add_argument("-n", "--number", action="store_true", help="Number all output lines, starting at 1.")
group.add_argument("-b", "--number-nonblank", action="store_true", help="Number non-empty output lines, starting at 1.")
parser.add_argument("paths", nargs='+', help="Files to read ('-' for stdin).")

return parser.parse_args()

def read_file_content(path):
try:
if path == "-":
return sys.stdin.read()
with open(path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
print(f"Error reading {path}: {e}", file=sys.stderr)
return ""

def extract_content_lines(content):
return content.splitlines()

def output_lines(lines, number_all, number_nonblank):
line_number = 1
for line in lines:
if number_all:
print(f"{line_number:6}\t{line}")
line_number += 1
elif number_nonblank:
if line.strip() != "":
print(f"{line_number:6}\t{line}")
line_number += 1
else:
print()
else:
print(line)

def main():
args = setup_arguments()
all_lines = []

for path in args.paths:
content = read_file_content(path)
lines = extract_content_lines(content)
all_lines.extend(lines)

output_lines(all_lines, args.number, args.number_nonblank)

if __name__ == "__main__":
main()
53 changes: 53 additions & 0 deletions implement-shell-tools/ls/ls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import argparse
import os

def setup_arguments():
parser = argparse.ArgumentParser(
prog="custom_ls",
description="Lists contents of a directory"
)
parser.add_argument(
"path", nargs="?", default=".",
help="Path to the target directory (defaults to current)"
)
parser.add_argument(
"-1", dest="linewise", action="store_true",
help="Display one entry per line"
)
parser.add_argument(
"-a", dest="include_hidden", action="store_true",
help="Include hidden files in the output"
)
return parser.parse_args()

def list_directory(path, show_hidden, one_per_line):
try:
items = os.listdir(path)
except FileNotFoundError:
print(f"Error: '{path}' not found.")
return
except NotADirectoryError:
print(f"Error: '{path}' is not a directory.")
return

entries = [
entry for entry in sorted(items)
if show_hidden or not entry.startswith(".")
]

if one_per_line:
for entry in entries:
print(entry)
else:
print(" ".join(entries))

def main():
args = setup_arguments()
list_directory(
path=args.path,
show_hidden=args.include_hidden,
one_per_line=args.linewise
)

if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions implement-shell-tools/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions implement-shell-tools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "py-cli-tools",
"version": "1.0.0"
}

75 changes: 75 additions & 0 deletions implement-shell-tools/wc/wc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import argparse

def setup_arguments():
parser = argparse.ArgumentParser(
prog="wordcount",
description="A simple wc clone that reads files and displays line, word, and character counts."
)

parser.add_argument("files", nargs='+', help="Files to analyze")
parser.add_argument("-l", "--lines", action="store_true", help="Count lines")
parser.add_argument("-w", "--words", action="store_true", help="Count words")
parser.add_argument("-c", "--chars", action="store_true", help="Count characters")

return parser.parse_args()

def read_text_from_file(path):
with open(path, "r") as f:
return f.read()

def count_lines(text):
return len(text.rstrip('\n').split('\n'))

def count_words(text):
return len(text.split())

def count_characters(text):
return len(text)

def analyze_file(path):
data = read_text_from_file(path)
return {
"lines": count_lines(data),
"words": count_words(data),
"chars": count_characters(data)
}

def show_counts(counts, path, opts):
if opts.lines:
print(counts["lines"], path)
elif opts.words:
print(counts["words"], path)
elif opts.chars:
print(counts["chars"], path)
else:
print(counts["lines"], counts["words"], counts["chars"], path)

def show_total(all_counts, opts):
total = {
"lines": sum(c["lines"] for c in all_counts),
"words": sum(c["words"] for c in all_counts),
"chars": sum(c["chars"] for c in all_counts)
}
if opts.lines:
print(total["lines"], "total")
elif opts.words:
print(total["words"], "total")
elif opts.chars:
print(total["chars"], "total")
else:
print(total["lines"], total["words"], total["chars"], "total")

def main():
options = setup_arguments()
all_results = []

for file in options.files:
result = analyze_file(file)
all_results.append(result)
show_counts(result, file, options)

if len(options.files) > 1:
show_total(all_results, options)

if __name__ == "__main__":
main()