Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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,2 +1,3 @@
node_modules
venv
testoutput.txt
1 change: 1 addition & 0 deletions implement-shell-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.venv/
76 changes: 76 additions & 0 deletions implement-shell-tools/cat/mycat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3

import sys
import glob


def expand_paths(paths):

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.

You shouldn't need to do this expansion in your program; the shell should do it for you.

"""Expand glob patterns and return sorted unique file list."""
files = []
for p in paths:
matches = glob.glob(p)
if matches:
files.extend(matches)
else:
files.append(p) # keep as-is (will error later if missing)
return sorted(files)

def read_lines(file):
with open(file, "r", encoding="utf-8") as f:
Comment thread
illicitonion marked this conversation as resolved.
Outdated
return f.readlines()

def print_lines(files, number_all=False, number_nonempty=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.
continue

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

if number_nonempty:
if not is_empty:
prefix = f"{line_no:6}\t"
line_no += 1
else:
prefix = ""
elif number_all:
prefix = f"{line_no:6}\t"
line_no += 1
else:
prefix = ""

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


def main():
Comment thread
illicitonion marked this conversation as resolved.
Outdated
args = sys.argv[1:]

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

number_all = False
number_nonempty = False
paths = []

for a in args:
if a == "-n":
number_all = True
elif a == "-b":
number_nonempty = True
number_all = False #-b overrides -n
Comment thread
illicitonion marked this conversation as resolved.
Outdated
else:
paths.append(a)

files = expand_paths(paths)
print_lines(files, number_all,number_nonempty)
Comment thread
illicitonion marked this conversation as resolved.
Outdated
Comment thread
illicitonion marked this conversation as resolved.
Outdated


if __name__ == "__main__":
main()

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

import sys
import os

def list_dir(path, show_all=False, one_per_line = False):
try:
entries = os.listdir(path)
except FileNotFoundError:
Comment thread
illicitonion marked this conversation as resolved.
Outdated
print(f"ls: cannot access '{path}': No such file or directory", file=sys.stderr)
return

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)

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 = ["."]

for path in paths:
list_dir(path, show_all, one_per_line)

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
import glob

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 expand(paths):
Comment thread
illicitonion marked this conversation as resolved.
Outdated
files = []
for p in paths:
matches = glob.glob(p)
if matches:
files.extend(matches)
else:
files.append(p)
return files

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 = expand(paths)

total_l = 0
total_w = 0
total_c = 0

results = []

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)
Comment thread
illicitonion marked this conversation as resolved.
Outdated
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}")


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


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