Skip to content

Commit a93b794

Browse files
committed
cat-ls-wc Python
1 parent 407b010 commit a93b794

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python3
2+
import sys
3+
import argparse
4+
5+
def read_file(path, number_all, number_nonblank, line_counter):
6+
"""Reads a file and prints its contents with the requested numbering."""
7+
try:
8+
with open(path, "r") as f:
9+
for line in f:
10+
line_no_newline = line.rstrip("\n")
11+
12+
if number_all:
13+
print(f"{line_counter:6}\t{line_no_newline}")
14+
line_counter += 1
15+
16+
elif number_nonblank:
17+
if line_no_newline.strip():
18+
print(f"{line_counter:6}\t{line_no_newline}")
19+
line_counter += 1
20+
else:
21+
print("") # blank line stays blank
22+
23+
else:
24+
print(line_no_newline)
25+
26+
return line_counter
27+
28+
except FileNotFoundError:
29+
print(f"cat: {path}: No such file or directory", file=sys.stderr)
30+
return line_counter
31+
32+
33+
def main():
34+
parser = argparse.ArgumentParser(description="Simple cat implementation")
35+
parser.add_argument("-n", action="store_true", help="number all lines")
36+
parser.add_argument("-b", action="store_true", help="number non-blank lines")
37+
parser.add_argument("files", nargs="+", help="files to read")
38+
39+
args = parser.parse_args()
40+
41+
# cat rule: if -b is used, ignore -n
42+
number_all = args.n
43+
number_nonblank = args.b
44+
if number_nonblank:
45+
number_all = False
46+
47+
line_counter = 1
48+
49+
for path in args.files:
50+
line_counter = read_file(path, number_all, number_nonblank, line_counter)
51+
52+
53+
if __name__ == "__main__":
54+
main()
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import argparse
6+
7+
8+
def list_directory(path, show_all):
9+
"""List contents of a directory, respecting the -a and -1 options."""
10+
11+
try:
12+
entries = os.listdir(path)
13+
except FileNotFoundError:
14+
print(f"ls: cannot access '{path}': No such file or directory", file=sys.stderr)
15+
return
16+
17+
# If -a is not provided, hide dotfiles
18+
if not show_all:
19+
entries = [e for e in entries if not e.startswith(".")]
20+
21+
# Sort alphabetically like ls normally does
22+
entries.sort()
23+
24+
# Print one entry per line (-1 behaviour)
25+
for entry in entries:
26+
print(entry)
27+
28+
29+
def main():
30+
parser = argparse.ArgumentParser(description="Simple ls implementation")
31+
parser.add_argument(
32+
"-a",
33+
action="store_true",
34+
help="include directory entries whose names begin with a dot",
35+
)
36+
parser.add_argument(
37+
"-1",
38+
dest="one_per_line",
39+
action="store_true",
40+
help="list one file per line",
41+
)
42+
parser.add_argument("path", nargs="?", default=".", help="directory to list")
43+
44+
args = parser.parse_args()
45+
46+
# Only -1 is supported, but it's always required in this assignment
47+
if not args.one_per_line:
48+
print("This program only supports the -1 option.", file=sys.stderr)
49+
sys.exit(1)
50+
51+
list_directory(args.path, show_all=args.a)
52+
53+
54+
if __name__ == "__main__":
55+
main()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import sys
4+
5+
6+
def count_file(path):
7+
"""Return (lines, words, bytes) for the given file."""
8+
try:
9+
with open(path, "rb") as f:
10+
content = f.read()
11+
except FileNotFoundError:
12+
print(f"wc: {path}: No such file or directory", file=sys.stderr)
13+
return None
14+
15+
text = content.decode("utf-8", errors="replace")
16+
lines = text.count("\n")
17+
words = len(text.split())
18+
bytes_ = len(content)
19+
20+
return lines, words, bytes_
21+
22+
23+
def print_result(counts, path, show_l, show_w, show_c):
24+
"""Format and print output for a file."""
25+
lines, words, bytes_ = counts
26+
27+
# If no specific flag is given → print all three (like wc)
28+
if not (show_l or show_w or show_c):
29+
print(f"{lines:>7} {words:>7} {bytes_:>7} {path}")
30+
return
31+
32+
out = []
33+
if show_l:
34+
out.append(f"{lines:>7}")
35+
if show_w:
36+
out.append(f"{words:>7}")
37+
if show_c:
38+
out.append(f"{bytes_:>7}")
39+
40+
print(" ".join(out), path)
41+
42+
43+
def main():
44+
parser = argparse.ArgumentParser(description="Simple wc implementation")
45+
parser.add_argument("-l", action="store_true", help="count lines")
46+
parser.add_argument("-w", action="store_true", help="count words")
47+
parser.add_argument("-c", action="store_true", help="count bytes")
48+
parser.add_argument("paths", nargs="+", help="file(s) to process")
49+
50+
args = parser.parse_args()
51+
52+
total_lines = total_words = total_bytes = 0
53+
multiple_files = len(args.paths) > 1
54+
55+
for path in args.paths:
56+
result = count_file(path)
57+
if result is None:
58+
continue
59+
60+
lines, words, bytes_ = result
61+
print_result(result, path, args.l, args.w, args.c)
62+
63+
total_lines += lines
64+
total_words += words
65+
total_bytes += bytes_
66+
67+
# Print totals if more than one file
68+
if multiple_files:
69+
if not (args.l or args.w or args.c):
70+
print(f"{total_lines:>7} {total_words:>7} {total_bytes:>7} total")
71+
else:
72+
out = []
73+
if args.l:
74+
out.append(f"{total_lines:>7}")
75+
if args.w:
76+
out.append(f"{total_words:>7}")
77+
if args.c:
78+
out.append(f"{total_bytes:>7}")
79+
print(" ".join(out), "total")
80+
81+
82+
if __name__ == "__main__":
83+
main()

0 commit comments

Comments
 (0)