Skip to content
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
72 changes: 72 additions & 0 deletions implement-shell-tools/cat/mycat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python3

import sys

def main():
args = sys.argv[1:]

if not args:
print("Usage: cat [-n|-b] file...", file=sys.stderr)
sys.exit(1)

number_mode = "none"
paths = []

for a in args:
if a == "-n":
number_mode = "all"
elif a == "-b":
number_mode = "non_empty"
else:
paths.append(a)



had_error = print_lines(
paths,
number_mode=number_mode,
)

if had_error:
sys.exit(1)


def read_lines(file):
with open(file, "r", encoding="utf-8") as f:
return f.readlines()

def print_lines(files, number_mode="none"):
had_error = False
line_no = 1
for file in files:
try:
lines = read_lines(file)
except FileNotFoundError:
print(f"cat: {file}: No such file or directory", file=sys.stderr)
Comment thread
illicitonion marked this conversation as resolved.
had_error = True
continue

for line in lines:
is_empty = (line.strip() == "")

if number_mode == "non_empty":
if not is_empty:
prefix = f"{line_no:6}\t"
line_no += 1
else:
prefix = ""
elif number_mode=="all":
prefix = f"{line_no:6}\t"
line_no += 1
Comment on lines +52 to +60

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These branches look very similar - could they be combined?

else:
prefix = ""

# avoid double newlines: line already includes '\n'
sys.stdout.write(prefix + line)

return had_error

if __name__ == "__main__":
main()


65 changes: 65 additions & 0 deletions implement-shell-tools/ls/my-ls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3

import sys
import os



def main():
args = sys.argv[1:]

show_all = False
one_per_line = False
paths = []

for a in args:
if a == "-a":
show_all = True
elif a == "-1":
one_per_line = True
else:
paths.append(a)

if not paths:
paths = ["."]

had_error = False

for path in paths:
if list_dir(
path,
show_all=show_all,
one_per_line=one_per_line,
):
had_error = True

if had_error:
sys.exit(1)


def list_dir(path, show_all=False, one_per_line=False):
try:
entries = os.listdir(path)
except FileNotFoundError:
print(f"ls: cannot access '{path}': No such file or directory",
file=sys.stderr,
)
return True

entries = sorted(entries)

if show_all:
normal = sorted([e for e in entries if not e.startswith('.')])
hidden = sorted([e for e in entries if e.startswith('.')])

entries = [".", ".."] + normal + hidden
else:
entries = sorted([e for e in entries if not e.startswith('.')])

for entry in entries:
print(entry)

return False

if __name__ == "__main__":
main()
104 changes: 104 additions & 0 deletions implement-shell-tools/wc/my-wc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python3

import sys


def count_file(path):
with open(path, "rb") as f:
content = f.read()

byte_count = len(content)
text = content.decode("utf-8", errors="ignore")

line_count = text.count("\n")
word_count = len(text.split())

return line_count, word_count, byte_count



def main():
args = sys.argv[1:]

show_l = False
show_w = False
show_c = False

paths = []


# parse args
for a in args:
if a == "-l":
show_l = True
elif a == "-w":
show_w = True
elif a == "-c":
show_c = True
else:
paths.append(a)

# default: show all
if not (show_l or show_w or show_c):
show_l = show_w = show_c = True

files = paths

total_l = 0
total_w = 0
total_c = 0

results = []
had_error = False

for file in files:
try:
l, w, c = count_file(file)
except FileNotFoundError:
print(
f"wc: {file}: No such file or directory",
file=sys.stderr,
)
had_error = True
continue

total_l += l
total_w += w
total_c += c

results.append((l,w,c, file))

# print per-file results (GNU-aligned formatting)
for l, w, c, file in results:

parts = []
if show_l:
parts.append(f"{l:3}")
if show_w:
parts.append(f"{w:4}")
if show_c:
parts.append(f"{c:4}")


print("".join(parts) + " " + file)

# print total if multiple files
if len(results) > 1:
parts = []

if show_l:
parts.append(f"{total_l:3}")
if show_w:
parts.append(f"{total_w:4}")
if show_c:
parts.append(f"{total_c:4}")
Comment on lines +74 to +94

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These blocks feel quite repetitive. Can you work out how to deduplicate them?

One thing that may be useful to know is you can define nested functions which can access any variables within the scope they're defined in. e.g. you can write:

def outer():
  value = 1

  def inner(name):
    print(f"{name}: {value}")

  for letter in ["a", "b", "c"]:
    inner(letter)

and inner can access value.



print("".join(parts) + " total")

if had_error:
sys.exit(1)


if __name__ == "__main__":
main()
Loading