Skip to content

Commit 5b484f1

Browse files
committed
Completed implementing shell tools in python.
1 parent e328bd5 commit 5b484f1

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import argparse
2+
import sys
3+
4+
parser = argparse.ArgumentParser(
5+
description="Reads and prints one or more files, optionally numbering lines continuously"
6+
)
7+
parser.add_argument("paths", nargs='+', help="One or more file paths")
8+
parser.add_argument("-n", "--number", action="store_true", help="Number all output lines")
9+
parser.add_argument("-b", "--number-nonblank", action="store_true", help="Number non-empty output lines")
10+
11+
args = parser.parse_args()
12+
13+
file_paths = args.paths
14+
number_nonblank = args.number_nonblank
15+
number_all = args.number and not number_nonblank
16+
17+
line_number = 1
18+
19+
for path in file_paths:
20+
try:
21+
with open(path, 'r', encoding='utf-8') as f:
22+
lines = f.readlines()
23+
24+
if number_nonblank:
25+
nonblank_lines = [line for line in lines if line.strip()]
26+
max_digits = len(str(len(nonblank_lines)))
27+
28+
for line in lines:
29+
if line.strip() == "":
30+
print()
31+
else:
32+
num_str = str(line_number).rjust(max_digits)
33+
print(f"{num_str}\t{line.rstrip()}")
34+
line_number += 1
35+
36+
elif number_all:
37+
max_digits = len(str(len(lines) * len(file_paths)))
38+
39+
for line in lines:
40+
num_str = str(line_number).rjust(max_digits)
41+
print(f"{num_str}\t{line.rstrip()}")
42+
line_number += 1
43+
44+
else:
45+
for line in lines:
46+
print(line, end='')
47+
if not lines[-1].endswith('\n'):
48+
print()
49+
50+
except Exception as e:
51+
print(f'Error reading file "{path}": {e}', file=sys.stderr)
52+
sys.exit(1)

implement-shell-tools/ls/my_ls.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import os
2+
import sys
3+
import stat
4+
import argparse
5+
6+
parser = argparse.ArgumentParser(
7+
description="List files in a directory (simplified ls implementation)"
8+
)
9+
parser.add_argument("paths", nargs="*", default=["."], help="One or more file or directory paths")
10+
parser.add_argument("-l", "--longList", action="store_true", help="Long listing format")
11+
parser.add_argument("-a", "--all", action="store_true", help="Include hidden files")
12+
13+
args = parser.parse_args()
14+
15+
file_paths = args.paths
16+
show_long = args.longList
17+
show_all = args.all
18+
19+
def format_permissions(mode):
20+
return stat.filemode(mode)
21+
22+
for input_path in file_paths:
23+
try:
24+
if not os.path.exists(input_path):
25+
raise FileNotFoundError(f'No such file or directory: {input_path}')
26+
27+
if os.path.isfile(input_path):
28+
if show_long:
29+
file_stat = os.stat(input_path)
30+
perms = format_permissions(file_stat.st_mode)
31+
size = str(file_stat.st_size).rjust(6)
32+
print(f"{perms} {size} {input_path}")
33+
else:
34+
print(input_path)
35+
36+
elif os.path.isdir(input_path):
37+
entries = os.listdir(input_path)
38+
if not show_all:
39+
entries = [e for e in entries if not e.startswith(".")]
40+
41+
for entry in entries:
42+
full_path = os.path.join(input_path, entry)
43+
entry_stat = os.stat(full_path)
44+
if show_long:
45+
perms = format_permissions(entry_stat.st_mode)
46+
size = str(entry_stat.st_size).rjust(6)
47+
print(f"{perms} {size} {entry}")
48+
else:
49+
print(entry)
50+
51+
except Exception as e:
52+
print(f'Error reading "{input_path}": {e}', file=sys.stderr)
53+
sys.exit(1)

implement-shell-tools/wc/my_wc.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import os
2+
import sys
3+
import argparse
4+
5+
# CLI argument parsing
6+
parser = argparse.ArgumentParser(description="Simplified implementation of wc")
7+
parser.add_argument("paths", nargs="*", default=["."], help="One or more file or directory paths")
8+
parser.add_argument("-l", "--line", action="store_true", help="Count lines")
9+
parser.add_argument("-w", "--word", action="store_true", help="Count words")
10+
parser.add_argument("-c", "--character", action="store_true", help="Count characters")
11+
12+
args = parser.parse_args()
13+
file_paths = args.paths
14+
15+
# Fallback: if no options passed, show all
16+
show_line = args.line
17+
show_word = args.word
18+
show_char = args.character
19+
show_all = not (show_line or show_word or show_char)
20+
21+
# Count content in a string
22+
def count_content(content):
23+
lines = content.splitlines()
24+
words = content.strip().split()
25+
characters = len(content)
26+
return len(lines), len(words), characters
27+
28+
# Totals for multiple files
29+
total = {
30+
"lines": 0,
31+
"words": 0,
32+
"characters": 0
33+
}
34+
35+
file_count = 0
36+
37+
for input_path in file_paths:
38+
try:
39+
if os.path.isdir(input_path):
40+
print(f"{input_path} is a directory. Skipping.")
41+
continue
42+
43+
with open(input_path, "r", encoding="utf-8") as f:
44+
content = f.read()
45+
46+
lines, words, characters = count_content(content)
47+
48+
total["lines"] += lines
49+
total["words"] += words
50+
total["characters"] += characters
51+
file_count += 1
52+
53+
# Prepare output per file
54+
output_parts = []
55+
if show_line or show_all:
56+
output_parts.append(f"{lines:8}")
57+
if show_word or show_all:
58+
output_parts.append(f"{words:8}")
59+
if show_char or show_all:
60+
output_parts.append(f"{characters:8}")
61+
62+
output_parts.append(input_path)
63+
print(" ".join(output_parts))
64+
65+
except Exception as e:
66+
print(f'Error reading "{input_path}": {e}', file=sys.stderr)
67+
68+
# Print totals if more than one file processed
69+
if file_count > 1:
70+
output_parts = []
71+
if show_line or show_all:
72+
output_parts.append(f"{total['lines']:8}")
73+
if show_word or show_all:
74+
output_parts.append(f"{total['words']:8}")
75+
if show_char or show_all:
76+
output_parts.append(f"{total['characters']:8}")
77+
output_parts.append("total")
78+
print(" ".join(output_parts))

0 commit comments

Comments
 (0)