Skip to content

Commit b9a6825

Browse files
committed
implement-shell-tools-in-python
1 parent 4350f48 commit b9a6825

3 files changed

Lines changed: 181 additions & 0 deletions

File tree

implement-shell-tools/cat/cat.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import argparse
2+
import glob
3+
4+
# create argument parser
5+
parser = argparse.ArgumentParser(description="Simple cat tool")
6+
7+
parser.add_argument("files", nargs="+", help="files to read")
8+
parser.add_argument("-n", action="store_true", help="number all lines")
9+
parser.add_argument("-b", action="store_true", help="number non-empty lines")
10+
11+
args = parser.parse_args()
12+
13+
# if -b is used, ignore -n
14+
if args.b:
15+
args.n = False
16+
17+
line_number = 1
18+
19+
20+
# expand *.txt into real file names
21+
def get_files(file_patterns):
22+
files = []
23+
for pattern in file_patterns:
24+
matched = glob.glob(pattern)
25+
26+
if matched:
27+
files.extend(matched)
28+
else:
29+
files.append(pattern)
30+
31+
return files
32+
33+
34+
def print_file(file_name):
35+
global line_number
36+
37+
try:
38+
with open(file_name, "r") as f:
39+
for line in f:
40+
41+
# -b → number only non-empty lines
42+
if args.b:
43+
if line.strip(): # not empty
44+
print(f"{line_number}\t{line}", end="")
45+
line_number += 1
46+
else:
47+
print(line, end="")
48+
49+
# -n → number all lines
50+
elif args.n:
51+
print(f"{line_number}\t{line}", end="")
52+
line_number += 1
53+
54+
# no flags
55+
else:
56+
print(line, end="")
57+
58+
except FileNotFoundError:
59+
print(f"Error: {file_name} not found")
60+
61+
62+
# main program
63+
files = get_files(args.files)
64+
65+
for file in files:
66+
print_file(file)

implement-shell-tools/ls/ls.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import argparse
2+
import os
3+
4+
# create parser
5+
parser = argparse.ArgumentParser(description="Simple ls tool")
6+
7+
parser.add_argument("-1", dest="one", action="store_true", help="one item per line")
8+
parser.add_argument("-a", action="store_true", help="show hidden files")
9+
parser.add_argument("path", nargs="?", default=".", help="directory path")
10+
11+
args = parser.parse_args()
12+
13+
14+
def list_files(path):
15+
try:
16+
files = os.listdir(path)
17+
18+
# if -a is NOT used, hide files starting with "."
19+
if not args.a:
20+
files = [f for f in files if not f.startswith(".")]
21+
22+
# sort like real ls
23+
files.sort()
24+
25+
# print results
26+
if args.one:
27+
for f in files:
28+
print(f)
29+
else:
30+
print(" ".join(files))
31+
32+
except FileNotFoundError:
33+
print(f"Error: {path} not found")
34+
35+
36+
# run
37+
list_files(args.path)

implement-shell-tools/wc/wc.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import argparse
2+
import glob
3+
4+
# create parser
5+
parser = argparse.ArgumentParser(description="Simple wc tool")
6+
7+
parser.add_argument("files", nargs="+", help="files to read")
8+
parser.add_argument("-l", action="store_true", help="count lines")
9+
parser.add_argument("-w", action="store_true", help="count words")
10+
parser.add_argument("-c", action="store_true", help="count characters")
11+
12+
args = parser.parse_args()
13+
14+
15+
# expand *.txt
16+
def get_files(file_patterns):
17+
files = []
18+
for pattern in file_patterns:
19+
matched = glob.glob(pattern)
20+
if matched:
21+
files.extend(matched)
22+
else:
23+
files.append(pattern)
24+
return files
25+
26+
27+
def count_file(file_name):
28+
try:
29+
with open(file_name, "r") as f:
30+
text = f.read()
31+
32+
lines = text.count("\n")
33+
words = len(text.split())
34+
chars = len(text)
35+
36+
return lines, words, chars
37+
38+
except FileNotFoundError:
39+
print(f"Error: {file_name} not found")
40+
return 0, 0, 0
41+
42+
43+
def print_result(lines, words, chars, file_name):
44+
# if no flags → show all
45+
if not args.l and not args.w and not args.c:
46+
print(lines, words, chars, file_name)
47+
48+
else:
49+
output = []
50+
51+
if args.l:
52+
output.append(str(lines))
53+
if args.w:
54+
output.append(str(words))
55+
if args.c:
56+
output.append(str(chars))
57+
58+
output.append(file_name)
59+
print(" ".join(output))
60+
61+
62+
# main
63+
files = get_files(args.files)
64+
65+
total_lines = total_words = total_chars = 0
66+
67+
for file in files:
68+
lines, words, chars = count_file(file)
69+
70+
total_lines += lines
71+
total_words += words
72+
total_chars += chars
73+
74+
print_result(lines, words, chars, file)
75+
76+
# if multiple files → show total
77+
if len(files) > 1:
78+
print_result(total_lines, total_words, total_chars, "total")

0 commit comments

Comments
 (0)