Skip to content

Commit 3fc838d

Browse files
Refactor wc counting and error handling
1 parent d6641a7 commit 3fc838d

1 file changed

Lines changed: 117 additions & 57 deletions

File tree

  • implement-shell-tools/wc

‎implement-shell-tools/wc/wc.py‎

Lines changed: 117 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,147 @@
11
import argparse
22
import sys
33

4-
def wc(path, count_lines, count_words, count_bytes):
5-
"""Count lines, words, and bytes for a single file."""
6-
try:
7-
with open(path, 'r') as f:
8-
content = f.read()
9-
10-
lines = content.splitlines()
11-
words = content.split()
12-
13-
line_count = len(lines)
14-
word_count = len(words)
15-
byte_count = len(content.encode('utf-8'))
16-
17-
if not any([count_lines, count_words, count_bytes]):
18-
count_lines = True
19-
count_words = True
20-
count_bytes = True
214

22-
parts = []
23-
24-
if count_lines:
25-
parts.append(str(line_count))
26-
27-
if count_words:
28-
parts.append(str(word_count))
29-
30-
if count_bytes:
31-
parts.append(str(byte_count))
5+
def wc(path):
6+
try:
7+
with open(path, "rb") as file:
8+
content = file.read()
329

33-
print(' '.join(parts), path)
10+
line_count = content.count(b"\n")
11+
word_count = len(content.split())
12+
byte_count = len(content)
3413

3514
return line_count, word_count, byte_count
3615

3716
except FileNotFoundError:
3817
print(
3918
f"wc: {path}: No such file or directory",
40-
file=sys.stderr
19+
file=sys.stderr,
4120
)
42-
return (0, 0, 0)
43-
4421
except IsADirectoryError:
4522
print(
4623
f"wc: {path}: Is a directory",
47-
file=sys.stderr
24+
file=sys.stderr,
25+
)
26+
except PermissionError:
27+
print(
28+
f"wc: {path}: Permission denied",
29+
file=sys.stderr,
4830
)
49-
return (0, 0, 0)
31+
except OSError as error:
32+
print(
33+
f"wc: {path}: {error}",
34+
file=sys.stderr,
35+
)
36+
37+
return None
38+
39+
40+
def print_stats(
41+
line_count,
42+
word_count,
43+
byte_count,
44+
filename,
45+
show_lines,
46+
show_words,
47+
show_bytes,
48+
):
49+
parts = []
50+
51+
if show_lines:
52+
parts.append(f"{line_count:7d}")
53+
54+
if show_words:
55+
parts.append(f"{word_count:7d}")
56+
57+
if show_bytes:
58+
parts.append(f"{byte_count:7d}")
59+
60+
print("".join(parts), filename)
61+
5062

5163
def main():
52-
parser = argparse.ArgumentParser()
53-
parser.add_argument('-l', action='store_true', help='Count lines')
54-
parser.add_argument('-w', action='store_true', help='Count words')
55-
parser.add_argument('-c', action='store_true', help='Count bytes')
56-
parser.add_argument('paths', nargs='+', help='Files to count')
64+
parser = argparse.ArgumentParser(
65+
description="Print newline, word, and byte counts for files."
66+
)
67+
68+
parser.add_argument(
69+
"-l",
70+
action="store_true",
71+
help="print the newline count",
72+
)
73+
74+
parser.add_argument(
75+
"-w",
76+
action="store_true",
77+
help="print the word count",
78+
)
79+
80+
parser.add_argument(
81+
"-c",
82+
action="store_true",
83+
help="print the byte count",
84+
)
85+
86+
parser.add_argument(
87+
"paths",
88+
nargs="+",
89+
help="files to count",
90+
)
91+
5792
args = parser.parse_args()
5893

94+
# If no options are supplied, wc prints all three counts.
95+
show_lines = args.l
96+
show_words = args.w
97+
show_bytes = args.c
98+
99+
if not any((show_lines, show_words, show_bytes)):
100+
show_lines = True
101+
show_words = True
102+
show_bytes = True
103+
59104
total_lines = 0
60105
total_words = 0
61106
total_bytes = 0
62-
63-
multiple_files = len(args.paths) > 1
64-
show_all = not any([args.l, args.w, args.c])
107+
successful_files = 0
65108

66109
for path in args.paths:
67-
l, w, b = wc(path, args.l, args.w, args.c)
68-
total_lines += l
69-
total_words += w
70-
total_bytes += b
71-
72-
if multiple_files:
73-
parts = []
74-
75-
if args.l or show_all:
76-
parts.append(str(total_lines))
110+
counts = wc(path)
111+
112+
if counts is None:
113+
continue
114+
115+
line_count, word_count, byte_count = counts
116+
117+
total_lines += line_count
118+
total_words += word_count
119+
total_bytes += byte_count
120+
successful_files += 1
121+
122+
print_stats(
123+
line_count,
124+
word_count,
125+
byte_count,
126+
path,
127+
show_lines,
128+
show_words,
129+
show_bytes,
130+
)
77131

78-
if args.w or show_all:
79-
parts.append(str(total_words))
132+
if len(args.paths) > 1 and successful_files > 0:
133+
print_stats(
134+
total_lines,
135+
total_words,
136+
total_bytes,
137+
"total",
138+
show_lines,
139+
show_words,
140+
show_bytes,
141+
)
80142

81-
if args.c or show_all:
82-
parts.append(str(total_bytes))
143+
return 0 if successful_files == len(args.paths) else 1
83144

84-
print(' '.join(parts), 'total')
85145

86146
if __name__ == "__main__":
87-
main()
147+
sys.exit(main())

0 commit comments

Comments
 (0)