Skip to content

Commit e0e9ceb

Browse files
committed
Implement cat, ls and wc in Python
1 parent 0b0eefb commit e0e9ceb

3 files changed

Lines changed: 131 additions & 0 deletions

File tree

implement-shell-tools/cat/cat.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import argparse
2+
import sys
3+
4+
parser = argparse.ArgumentParser(
5+
prog="cat",
6+
description="Concatenate files and print on the standard output",
7+
)
8+
parser.add_argument("-n", "--number", action="store_true",
9+
help="Number all output lines")
10+
parser.add_argument("-b", "--number-nonblank", action="store_true",
11+
help="Number non-empty output lines, overrides -n")
12+
parser.add_argument("files", nargs="+", help="The files to print")
13+
14+
args = parser.parse_args()
15+
16+
counter = 0
17+
for path in args.files:
18+
with open(path, "r") as f:
19+
for line in f:
20+
if args.number_nonblank:
21+
if line.strip("\n") == "":
22+
sys.stdout.write(line)
23+
else:
24+
counter += 1
25+
sys.stdout.write(f"{counter:6}\t{line}")
26+
elif args.number:
27+
counter += 1
28+
sys.stdout.write(f"{counter:6}\t{line}")
29+
else:
30+
sys.stdout.write(line)

implement-shell-tools/ls/ls.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import argparse
2+
import os
3+
import sys
4+
5+
parser = argparse.ArgumentParser(prog="ls", description="List directory contents")
6+
parser.add_argument("-1", dest="one_per_line", action="store_true",
7+
help="List one file per line")
8+
parser.add_argument("-a", "--all", action="store_true",
9+
help="Do not ignore entries starting with .")
10+
parser.add_argument("paths", nargs="*", default=["."],
11+
help="The files or directories to list")
12+
13+
args = parser.parse_args()
14+
15+
16+
def entries(directory):
17+
names = os.listdir(directory)
18+
if args.all:
19+
names = names + [".", ".."]
20+
else:
21+
names = [name for name in names if not name.startswith(".")]
22+
return sorted(names)
23+
24+
25+
files = []
26+
directories = []
27+
for path in args.paths:
28+
if os.path.isdir(path):
29+
directories.append(path)
30+
elif os.path.exists(path):
31+
files.append(path)
32+
else:
33+
print(f"ls: cannot access '{path}': No such file or directory",
34+
file=sys.stderr)
35+
36+
files.sort()
37+
directories.sort()
38+
39+
show_headers = len(args.paths) > 1
40+
printed_anything = False
41+
42+
for path in files:
43+
print(path)
44+
printed_anything = True
45+
46+
for directory in directories:
47+
if show_headers:
48+
if printed_anything:
49+
print()
50+
print(f"{directory}:")
51+
for name in entries(directory):
52+
print(name)
53+
printed_anything = True

implement-shell-tools/wc/wc.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import argparse
2+
import os
3+
4+
parser = argparse.ArgumentParser(prog="wc", description="Print newline, word and byte counts")
5+
parser.add_argument("-l", "--lines", action="store_true", help="Print the newline counts")
6+
parser.add_argument("-w", "--words", action="store_true", help="Print the word counts")
7+
parser.add_argument("-c", "--bytes", action="store_true", help="Print the byte counts")
8+
parser.add_argument("files", nargs="+", help="The files to count")
9+
10+
args = parser.parse_args()
11+
12+
show_all = not (args.lines or args.words or args.bytes)
13+
show_lines = args.lines or show_all
14+
show_words = args.words or show_all
15+
show_bytes = args.bytes or show_all
16+
17+
rows = []
18+
total = [0, 0, 0]
19+
20+
for path in args.files:
21+
with open(path, "rb") as f:
22+
data = f.read()
23+
counts = [data.count(b"\n"), len(data.split()), len(data)]
24+
for i in range(3):
25+
total[i] += counts[i]
26+
rows.append((counts, path))
27+
28+
if len(args.files) > 1:
29+
rows.append((total, "total"))
30+
width = len(str(sum(os.path.getsize(path) for path in args.files)))
31+
else:
32+
width = 1
33+
34+
35+
def selected(counts):
36+
chosen = []
37+
if show_lines:
38+
chosen.append(counts[0])
39+
if show_words:
40+
chosen.append(counts[1])
41+
if show_bytes:
42+
chosen.append(counts[2])
43+
return chosen
44+
45+
46+
for counts, label in rows:
47+
columns = " ".join(f"{value:{width}}" for value in selected(counts))
48+
print(f"{columns} {label}")

0 commit comments

Comments
 (0)