Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
prep/
50 changes: 50 additions & 0 deletions implement-shell-tools/cat/cat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import sys
import argparse

# Setup argument parser
parser = argparse.ArgumentParser(
prog="cat",
description="Concatenate and display files"
)

parser.add_argument("-n", "--number", action="store_true", help="Number all output lines")
parser.add_argument("-b", "--number-nonblank", action="store_true", help="Number non-empty lines only")
parser.add_argument("paths", nargs='+', help="Files to read")

args = parser.parse_args()

line_number = 0 # Shared counter across all files

# Process each file
for path in args.paths: # LEVEL 1: for loop starts
with open(path, "r") as f: # LEVEL 2: inside for loop
content = f.read() # LEVEL 3: inside with block

# Check if numbering is needed
if args.number: # LEVEL 2: inside for loop
lines = content.split("\n") # LEVEL 3: inside if
numbered_lines = []

for index, line in enumerate(lines): # LEVEL 3: inside if
line_number = line_number + 1 # LEVEL 4: inside inner for
numbered_line = f"{line_number:6}\t{line}"
numbered_lines.append(numbered_line)

print("\n".join(numbered_lines)) # LEVEL 3: inside if

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you are doing the same thing in both if conditions, setting up a list of lines and then printing. Could you reduce some of the code duplication?


elif args.number_nonblank: # LEVEL 2: inside for loop
lines = content.split("\n") # LEVEL 3: inside elif
numbered_lines = []

for line in lines: # LEVEL 3: inside elif
if line.strip() == "": # LEVEL 4: inside inner for
numbered_lines.append(line)
else:
line_number = line_number + 1
numbered_line = f"{line_number:6}\t{line}"
numbered_lines.append(numbered_line)

print("\n".join(numbered_lines))

else: # LEVEL 2: inside for loop
print(content) # LEVEL 3: inside else
24 changes: 24 additions & 0 deletions implement-shell-tools/ls/ls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
import argparse

# Setup argument parser
parser = argparse.ArgumentParser(
prog="ls",
description="Lists contents of a directory"
)

parser.add_argument("-1", dest="one_column", action="store_true", help="List one file per line")
parser.add_argument("-a", "--all", action="store_true", help="Include hidden files")
parser.add_argument("path", nargs='?', default=".", help="Directory to list (default: current directory)")

args = parser.parse_args()

# List directory contents
files = os.listdir(args.path)

# Filter out hidden files unless -a flag is used
if not args.all:
files = [f for f in files if not f.startswith('.')]

for file in files:
print(file)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test this program without the -1 option?

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

# Setup argument parser
parser = argparse.ArgumentParser(
prog="wc",
description="Count lines, words, and characters"
)
parser.add_argument("paths", nargs='+', help="Files to count")
parser.add_argument("-l", "--lines", action="store_true", help="Count lines only")
parser.add_argument("-w", "--words", action="store_true", help="Count words only")
parser.add_argument("-c", "--chars", action="store_true", help="Count characters only")

args = parser.parse_args()

# Track totals
total_lines = 0
total_words = 0
total_chars = 0

for path in args.paths:
# Read the file
with open(path, "r") as f:
content = f.read()

# Count lines, words, characters
lines = len(content.rstrip('\n').split('\n'))
words = len(content.split())
chars = len(content)

# Add to totals
total_lines += lines
total_words += words
total_chars += chars

if args.lines:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test with multiple arguments given at once?

print(f"{lines:8} {path}")
elif args.words:
print(f"{words:8} {path}")
elif args.chars:
print(f"{chars:8} {path}")
else:
print(f"{lines:8}{words:8}{chars:8} {path}")


# Print totals if multiple files
if len(args.paths) > 1:
if args.lines:
print(f"{total_lines:8} total")
elif args.words:
print(f"{total_words:8} total")
elif args.chars:
print(f"{total_chars:8} total")
else:
print(f"{total_lines:8}{total_words:8}{total_chars:8} total")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see the repetition in the structure of the code here? Could that be reduced somehow?