Skip to content

Commit 454bfc8

Browse files
committed
feat(cat): refactor Python implementation for streaming and correctness
- Replace read-all-then-process with line-by-line streaming - Fix -b flag: only skip truly empty lines (not whitespace-only lines) - Fix line numbering: use tabs after number to match real cat format - Maintain line number across multiple files for -n and -b flags - Improve error handling with file-specific error messages
1 parent 29a7dde commit 454bfc8

1 file changed

Lines changed: 28 additions & 40 deletions

File tree

  • implement-shell-tools/cat

implement-shell-tools/cat/cat.py

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,41 @@
22
import argparse
33

44
def read_and_output_files():
5-
# 1. Setup Argument Parser (Equivalent to 'commander')
5+
# Setup Argument Parser (Equivalent to 'commander')
66
parser = argparse.ArgumentParser(description="Python implementation of a basic cat-like utility")
77
parser.add_argument("-n", "--number", action="store_true", help="number all output lines")
88
parser.add_argument("-b", "--number-nonblank", action="store_true", help="number only non-empty lines")
99
parser.add_argument("files", nargs="+", help="files to read")
1010

1111
args = parser.parse_args()
1212

13-
try:
14-
# 2. Read all file contents (Equivalent to Promise.all / fs.readFile)
15-
file_contents = []
16-
for file_path in args.files:
17-
with open(file_path, "r", encoding="utf-8") as f:
18-
file_contents.append(f.read())
19-
20-
concatenated_content = "".join(file_contents)
21-
22-
# 3. Process Logic
23-
if args.number:
24-
# -n logic: number all lines
25-
lines = concatenated_content.split("\n")
26-
output = []
27-
for index, line in enumerate(lines, start=1):
28-
# rjust(6) is equivalent to padStart(6)
29-
output.append(f"{str(index).rjust(6)} {line}")
30-
sys.stdout.write("\n".join(output))
31-
32-
elif args.number_nonblank:
33-
# -b logic: number only non-empty lines
34-
lines = concatenated_content.split("\n")
35-
output = []
36-
nonblank_line_number = 0
37-
for line in lines:
38-
if line.strip() == "":
39-
output.append(line)
40-
else:
41-
nonblank_line_number += 1
42-
output.append(f"{str(nonblank_line_number).rjust(6)} {line}")
43-
sys.stdout.write("\n".join(output))
13+
line_number = 1
4414

45-
else:
46-
# No flags: standard output
47-
sys.stdout.write(concatenated_content)
48-
49-
except Exception as err:
50-
print(f"Error reading multiple files: {err}", file=sys.stderr)
51-
sys.exit(1)
15+
for file_path in args.files:
16+
try:
17+
with open(file_path, "r", encoding="utf-8") as f:
18+
for line in f:
19+
# 1. Handle -b logic (number only non-blank lines)
20+
if args.number_nonblank:
21+
# A truly blank line is JUST a newline character (\n or \r\n)
22+
if line == "\n" or line == "\r\n":
23+
sys.stdout.write(line)
24+
else:
25+
# Standard cat uses a tab (\t) after the line number
26+
sys.stdout.write(f"{str(line_number).rjust(6)}\t{line}")
27+
line_number += 1
28+
29+
# 2. Handle -n logic (number all lines)
30+
elif args.number:
31+
sys.stdout.write(f"{str(line_number).rjust(6)}\t{line}")
32+
line_number += 1
33+
34+
# 3. Handle standard output
35+
else:
36+
sys.stdout.write(line)
37+
38+
except Exception as err:
39+
print(f"cat: {file_path}: {err}", file=sys.stderr)
5240

5341
if __name__ == "__main__":
54-
read_and_output_files()
42+
read_and_output_files()

0 commit comments

Comments
 (0)