|
| 1 | +#!/usr/bin/env node |
1 | 2 | import { program } from "commander"; |
2 | 3 | import { promises as fs } from "node:fs"; |
3 | | -//import process from "node:process"; |
4 | 4 |
|
5 | 5 | // Setup CLI with commander |
6 | 6 | program |
7 | 7 | .name("count-containing-words") |
8 | 8 | .description("Counts words in a file that contain a particular character") |
9 | 9 | .option("-c, --char <char>", "The character to search for", "e") |
10 | | - .argument("<path>", "The file path to process"); |
| 10 | + .option("-n, --number", "Number all lines like cat -n") |
| 11 | + .option("-b, --number-nonblank", "Number only non-blank lines like cat -b") |
| 12 | + .argument("<path...>", "The file path(s) to process"); // allow multiple files |
11 | 13 |
|
12 | | -// Parse CLI arguments |
13 | 14 | program.parse(); |
14 | 15 | const argv = program.args; |
15 | 16 |
|
16 | 17 | // Validate arguments |
17 | | -if (argv.length != 1) { |
18 | | - console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`); |
| 18 | +if (argv.length === 0) { |
| 19 | + console.error(`Expected at least 1 file path to be passed but got ${argv.length}.`); |
19 | 20 | process.exit(1); |
20 | 21 | } |
21 | 22 |
|
| 23 | +const { char, number, numberNonblank } = program.opts(); |
| 24 | +let globalLineCounter = 1; |
22 | 25 |
|
23 | | -const path = argv[0]; |
24 | | -const char = program.opts().char; |
| 26 | +for (const path of argv) { |
| 27 | + let content; |
| 28 | + try { |
| 29 | + content = await fs.readFile(path, "utf-8"); |
| 30 | + } catch (err) { |
| 31 | + console.error(`Cannot read file ${path}: ${err.message}`); |
| 32 | + continue; |
| 33 | + } |
25 | 34 |
|
26 | | -// Read file content |
27 | | -const content = await fs.readFile(path, "utf-8"); |
28 | | -console.log("content",content) |
| 35 | + // Split file into lines |
| 36 | + let lines = content.split("\n"); |
29 | 37 |
|
30 | | -// Count words containing the character |
31 | | -const countOfWordsContainingChar = content |
32 | | - .split(" ") |
33 | | - .filter((word) => word.includes(char)) |
34 | | - .length; |
35 | | -console.log(countOfWordsContainingChar); |
| 38 | + // Remove last empty line if file ends with a newline |
| 39 | + if (lines[lines.length - 1] === "") { |
| 40 | + lines = lines.slice(0, -1); |
| 41 | + } |
36 | 42 |
|
| 43 | + // Process each line |
| 44 | + for (let line of lines) { |
| 45 | + // Count words containing the specified character in this line |
| 46 | + const countInLine = line |
| 47 | + .split(" ") |
| 48 | + .filter(word => word.includes(char)) |
| 49 | + .length; |
| 50 | + |
| 51 | + // Determine line numbering |
| 52 | + if (number) { |
| 53 | + console.log(`${String(globalLineCounter).padStart(6)}\t${line} [${countInLine}]`); |
| 54 | + globalLineCounter++; |
| 55 | + } else if (numberNonblank) { |
| 56 | + if (line.trim().length > 0) { |
| 57 | + console.log(`${String(globalLineCounter).padStart(6)}\t${line} [${countInLine}]`); |
| 58 | + globalLineCounter++; |
| 59 | + } else { |
| 60 | + console.log(`${line} [${countInLine}]`); |
| 61 | + } |
| 62 | + } else { |
| 63 | + console.log(`${line} [${countInLine}]`); |
| 64 | + } |
| 65 | + } |
| 66 | +} |
0 commit comments