Skip to content

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Luke-Manyamazi
Copy link

@Luke-Manyamazi Luke-Manyamazi commented Aug 5, 2025

Learners, PR Template

Self checklist

  • I have committed my files one by one, on purpose, and for a reason
  • I have titled my PR with Region | Cohort | FirstName LastName | Sprint | Assignment Title
  • I have tested my changes
  • My changes follow the style guide
  • My changes meet the requirements of this task

Changelist

This PR implements basic versions of the following Unix commands in Python:

  • cat: Concatenates and displays file contents, with support for -n (number all lines) and -b (number non-empty lines).
  • ls: Lists directory contents, supporting options for showing hidden files and listing in one column.
  • wc: Counts lines, words, and bytes in a file, with flags to show specific counts.

These scripts were designed to mimic standard command-line tools and handle typical edge cases like missing files and glob patterns.

Questions

  1. Are there any improvements you would suggest for handling edge cases (e.g., invalid file paths, empty files)?
  2. Is the use of argparse and glob in each script appropriate and idiomatic?
  3. Would you recommend any refactoring for readability or modularity?
  4. Are there any Python best practices I may have missed?

@Luke-Manyamazi Luke-Manyamazi added the Needs Review Trainee to add when requesting review. PRs without this label will not be reviewed. label Aug 5, 2025
@illicitonion illicitonion added Review in progress This review is currently being reviewed. This label will be replaced by "Reviewed" soon. and removed Needs Review Trainee to add when requesting review. PRs without this label will not be reviewed. labels Aug 6, 2025
Copy link
Member

@illicitonion illicitonion left a comment

Choose a reason for hiding this comment

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

This is generally looking good, but I left a few things to think about :)

import glob
import argparse

def cat(filepath, n=False, b=False, line_counter=None):
Copy link
Member

Choose a reason for hiding this comment

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

n and b are both:

  • Not very expressive names
  • Seem mutually exclusive - it doesn't make sense for someone to pass both.

Can you think of a way of passing this information into the function which is easier to read, and makes more clear that only one of these should be set?

else:
print(line, end='')
elif n:
print(f"{line_counter[0]:6}\t{line}", end='')
Copy link
Member

Choose a reason for hiding this comment

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

There's some repetition here between the b case and n case - imagine we wanted to start padding the line number by 8 instead of 6 characters - we'd need to change that in two places. Can you think how to avoid that?


files = []
for pattern in args.files:
files.extend(glob.glob(pattern) or [pattern])
Copy link
Member

Choose a reason for hiding this comment

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

It's interesting you're globbing here - I would expect a shell to handle this. What would break if you removed the glob expansion yourself?

if b:
if line.strip():
print(f"{line_counter[0]:6}\t{line}", end='')
line_counter[0] += 1
Copy link
Member

Choose a reason for hiding this comment

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

Why is this value in an array? You always seem to look at exactly one value in the array?

import glob
import argparse

def cat(filepath, n=False, b=False, line_counter=None):
Copy link
Member

Choose a reason for hiding this comment

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

It looks like you always pass values for these arguments - why are you supplying default values for them? Particularly for line_counter where if None is used your code will actually break.

except FileNotFoundError:
print(f"cat: {filepath}: No such file or directory")

def main():
Copy link
Member

Choose a reason for hiding this comment

The 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 cat -n /file/1 /file/2 and cat -b /file/1 /file/2 and compared the output with using your script, and didn't always get the same results

import os
import argparse

def ls(path='.', one_column=False, show_hidden=False):
Copy link
Member

Choose a reason for hiding this comment

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

Same question as above about default args

if count_words: parts.append(str(len(words)))
if count_bytes: parts.append(str(bytes_))

if not parts:
Copy link
Member

Choose a reason for hiding this comment

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

This works, but it feels like it's testing for a side-effect ("We didn't add any paths") rather than the cause ("you didn't ask for any of lines, words, or bytes") - I'd maybe rewrite this if to be framed in what the user requested.

(But the code works fine as it is, too)

parser.add_argument('paths', nargs='+', help='Files to count')
args = parser.parse_args()

for path in args.paths:
Copy link
Member

Choose a reason for hiding this comment

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

If I pass multiple files, the real wc outlines a line that you don't - can you add that too?

@illicitonion illicitonion added Reviewed Volunteer to add when completing a review with trainee action still to take. and removed Review in progress This review is currently being reviewed. This label will be replaced by "Reviewed" soon. labels Aug 6, 2025
@Luke-Manyamazi Luke-Manyamazi force-pushed the python-shell-tools-exercises branch from 35a4349 to ff86f10 Compare August 14, 2025 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Reviewed Volunteer to add when completing a review with trainee action still to take.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

01 Implement shell tools (cat, ls, wc) in Python
2 participants