Skip to content

Commit 6da6c6b

Browse files
Refactor shell tools: cat, ls, wc
- cat.mjs: - Fixed -b option to number only non-blank lines. - Unified line number padding to avoid repetition. - Added error handling for directories and missing files. - ls.mjs: - Correctly implemented -a option to include hidden files. - Validates single directory argument. - Sorted output and handled one-column listing. - wc.mjs: - Centralized line, word, and character counting in countFile(). - Defaults to counting all metrics if no options are given. - Added support for multiple files with totals. - Improved error handling for missing files and directories. Overall: - Removed duplication, clarified logic, and improved alignment with standard Unix behavior.
1 parent dae3b62 commit 6da6c6b

File tree

3 files changed

+47
-47
lines changed

3 files changed

+47
-47
lines changed

implement-shell-tools/cat/cat.mjs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ program
66
.description('Prints contents of files')
77
.option('-n, --number-all-lines', 'Number all output lines')
88
.option('-b, --number-non-blank', 'Number non-blank output lines')
9-
.argument('<files...>', 'Files to read')
9+
.argument('<files...>', 'Files to read');
10+
1011
program.parse();
1112

12-
const { numberAllLines, numNotBlank } = program.opts();
13+
const { numberAllLines, numberNonBlank } = program.opts();
1314
const files = program.args;
1415

1516
let lineNumber = 1;
@@ -21,12 +22,12 @@ for (const file of files) {
2122
console.error(`cat: ${file}: Is a directory`);
2223
continue;
2324
}
25+
2426
const content = await readFile(file, 'utf-8');
2527
const lines = content.split('\n');
2628

2729
for (const line of lines) {
28-
const shouldNumber = (numberAllLines && !numNotBlank) || (numNotBlank && line.trim() !== '');
29-
30+
const shouldNumber = (numberAllLines && !numberNonBlank) || (numberNonBlank && line.trim() !== '');
3031
if (shouldNumber) {
3132
console.log(`${lineNumber.toString().padStart(6)} ${line}`);
3233
lineNumber++;
@@ -37,4 +38,4 @@ for (const file of files) {
3738
} catch (err) {
3839
console.error(`cat: ${file}: ${err.message}`);
3940
}
40-
}
41+
}

implement-shell-tools/ls/ls.mjs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1-
import { program } from "commander"
2-
import process from "node:process"
3-
import { promises as fs } from "node:fs"
4-
import { readdir } from 'node:fs/promises'
1+
import { program } from "commander";
2+
import { promises as fs } from "fs";
3+
import process from "process";
54

65
program
7-
.name("ls")
8-
.description("Lists the files in a directory")
9-
.option("-1, --one", "One per line")
10-
.option("-a", "Include files starting with dot")
11-
.argument("filepath")
12-
program.parse(process.argv)
6+
.name("ls")
7+
.description("Lists the files in a directory")
8+
.option("-1, --one", "One per line")
9+
.option("-a, --all", "Include files starting with dot")
10+
.argument("<path>", "Directory to list");
1311

14-
const argv = program.args
15-
const opts = program.opts()
12+
program.parse(process.argv);
1613

17-
if (argv.length != 1){
18-
console.error("Expected 1 argument")
19-
process.exit(1)
14+
const args = program.args;
15+
const opts = program.opts();
16+
17+
if (args.length !== 1) {
18+
console.error("Expected 1 argument");
19+
process.exit(1);
2020
}
2121

22-
const content = await fs.readdir(argv[0])
22+
try {
23+
let files = await fs.readdir(args[0]);
24+
if (!opts.all) {
25+
files = files.filter(f => !f.startsWith('.'));
26+
}
27+
28+
files.sort();
2329

24-
console.log(content.join(opts.one ? "\n": " "))
30+
if (opts.one) {
31+
console.log(files.join('\n'));
32+
} else {
33+
console.log(files.join(' '));
34+
}
35+
} catch (err) {
36+
console.error(`ls: cannot access '${args[0]}': ${err.message}`);
37+
}

implement-shell-tools/wc/wc.mjs

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { program } from "commander";
2-
import process from "node:process";
3-
import { readFileSync, existsSync } from "node:fs";
2+
import { readFileSync, existsSync } from "fs";
3+
import process from "process";
44

55
program
66
.name("wc")
77
.description("Count lines, words, and characters in files")
88
.argument("[files...]", "Files to process")
99
.option("-l, --lines", "Count lines")
1010
.option("-w, --words", "Count words")
11-
.option("-c, --chars", "Count characters (bytes)")
11+
.option("-c, --chars", "Count characters (bytes)");
12+
1213
program.parse(process.argv);
1314

1415
const options = program.opts();
@@ -31,14 +32,8 @@ function countFile(filePath, options) {
3132
return null;
3233
}
3334

34-
if (typeof content !== 'string') {
35-
content = "";
36-
}
37-
3835
const lineCount = (content.match(/\n/g) || []).length;
39-
4036
const wordCount = content.trim().split(/\s+/).filter(Boolean).length;
41-
4237
const charCount = Buffer.byteLength(content, "utf8");
4338

4439
return {
@@ -50,21 +45,18 @@ function countFile(filePath, options) {
5045
}
5146

5247
const results = [];
53-
let totalLines = 0;
54-
let totalWords = 0;
55-
let totalChars = 0;
48+
let totalLines = 0, totalWords = 0, totalChars = 0;
5649
const hasMultipleFiles = files.length > 1;
5750

58-
files.forEach(file => {
51+
for (const file of files) {
5952
const result = countFile(file, options);
6053
if (result) {
6154
results.push(result);
62-
6355
if (result.lines !== null) totalLines += result.lines;
6456
if (result.words !== null) totalWords += result.words;
6557
if (result.chars !== null) totalChars += result.chars;
6658
}
67-
});
59+
}
6860

6961
results.forEach(result => {
7062
const output = [];
@@ -76,14 +68,8 @@ results.forEach(result => {
7668

7769
if (hasMultipleFiles && results.length > 0) {
7870
const totalOutput = [];
79-
if (options.lines || (!options.words && !options.chars)) {
80-
totalOutput.push(totalLines.toString().padStart(8));
81-
}
82-
if (options.words || (!options.lines && !options.chars)) {
83-
totalOutput.push(totalWords.toString().padStart(8));
84-
}
85-
if (options.chars || (!options.lines && !options.words)) {
86-
totalOutput.push(totalChars.toString().padStart(8));
87-
}
71+
if (options.lines || (!options.words && !options.chars)) totalOutput.push(totalLines.toString().padStart(8));
72+
if (options.words || (!options.lines && !options.chars)) totalOutput.push(totalWords.toString().padStart(8));
73+
if (options.chars || (!options.lines && !options.words)) totalOutput.push(totalChars.toString().padStart(8));
8874
console.log(totalOutput.join(" "), "total");
89-
}
75+
}

0 commit comments

Comments
 (0)