Skip to content

Commit 4c263a8

Browse files
Refactor 'cat', 'ls', and 'wc' commands for improved readability and functionality; add package-lock.json and package.json for dependency management
1 parent f82bbb0 commit 4c263a8

File tree

5 files changed

+115
-74
lines changed

5 files changed

+115
-74
lines changed

implement-shell-tools/cat/cat.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,36 @@ import { program } from "commander";
22
import { promises as fs } from "node:fs";
33
import process from "node:process";
44

5+
// configure the CLI program with its name, description, arguments, options, and actions (the help instructions)
56
// configure the CLI program with its name, description, arguments, options, and actions (the help instructions)
67
program
78
.name("cat")
89
.description("An alternative to the 'cat' command")
910
.argument("<files...>", "The file(s) to process")
1011
.option("-n, --number", "Number all output lines")
1112
.option("-b, --number-nonblank", "Number non-blank output lines")
12-
// actions to process the provided files with the specified options (-n, -b)
1313
.action(async (files, options) => {
1414
try {
15-
// call newCat for all files
16-
await newCat(files, options.number, options.numberNonblank)
15+
await newCat(files, options);
1716
} catch (err) {
18-
console.error(`Error: ${err.message}`);
17+
console.error(`Error: ${err.message}`);
1918
}
20-
});
19+
});
2120

22-
// parse command-line file arguments using the process.argv array
2321
program.parse(process.argv);
2422

25-
function printNumberedLine(line, lineNumber) {
26-
console.log(`${lineNumber.toString().padStart(6, ' ')} ${line}`);
27-
return lineNumber + 1;
23+
//helper function to format output
24+
function printLine(line, lineNumber, padWidth) {
25+
if (lineNumber !== null) {
26+
console.log(`${lineNumber.toString().padStart(padWidth, ' ')} ${line}`);
27+
} else {
28+
console.log(line);
29+
}
2830
}
2931

30-
async function newCat(files, numberLines, numberNonBlank) {
32+
async function newCat(files, options) {
3133
let lineNumber = 1;
34+
const padWidth = 6;
3235

3336
for (const file of files) {
3437
// read each file into a single text string
@@ -43,24 +46,21 @@ async function newCat(files, numberLines, numberNonBlank) {
4346
lines.pop();
4447
}
4548

46-
for (const line of lines) {
47-
if (numberNonBlank) {
48-
// check what is left on the line after trimming (truthy = text, falsy = blank)
49-
if (line.trim()) {
50-
lineNumber = printNumberedLine(line, lineNumber);
51-
} else {
52-
console.log(line)
53-
}
54-
} else if (numberLines) {
49+
lines.forEach(line => {
50+
//line trim: truthy = text, falsy = blank
51+
if (options.numberNonblank && line.trim()) {
52+
// number non-blank lines only
53+
printLine(line, lineNumber++, padWidth);
54+
} else if (options.number){
5555
// number all lines
56-
lineNumber = printNumberedLine(line, lineNumber);
56+
printLine(line, lineNumber++, padWidth);
5757
} else {
58-
// if neither flag print normally
59-
console.log(line)
58+
// neither flag, print normally
59+
printLine(line, null, padWidth)
6060
}
61-
}
61+
});
6262
} catch (err) {
63-
console.error(`Error reading file ${file}: ${err.message}`);
63+
console.error(`Error reading file ${file}: ${err.message}`);
6464
}
6565
}
6666
}

implement-shell-tools/ls/ls.js

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,77 +11,80 @@ program
1111
.option("-1", "List all files, one per line")
1212
.option("-a, --all", "Include hidden files (those starting with .) in the listing")
1313
.action(async (directory, options) => {
14-
if (program.args.length > 1) {
15-
console.error(`Expected no more than 1 argument (sample-files) but got ${program.args.length}.`);
16-
process.exit(1);
17-
}
14+
try {
15+
// default to current directory if none is specified
16+
const dir = directory || ".";
1817

19-
// default directory to current folder
20-
directory = directory || ".";
21-
22-
await newLs(directory, options['1'], options.all);
18+
await newLs(dir, options['1'], options.all);
19+
} catch (err) {
20+
console.error(`Error: ${err.message}`);
21+
}
2322
});
2423

2524
program.parse(process.argv);
2625

2726

27+
// filter files based on visibility (includeHidden = true includes all files)
28+
function filterFiles(entries, includeHidden) {
29+
return entries.filter(name =>
30+
includeHidden ? true : !name.startsWith(".")
31+
);
32+
}
2833

34+
// sort entries: directories first, then files,
2935
function sortEntries(entries) {
36+
const dirs = entries.filter(entry => {
37+
try {
38+
return fs.statSync(entry).isDirectory();
39+
} catch (err) {
40+
return false;
41+
}
42+
});
43+
44+
const files = entries.filter(entry => {
45+
try {
46+
return fs.statSync(entry).isFile();
47+
} catch (err) {
48+
return false;
49+
}
50+
});
3051
// localeCompare = take into account rules of system language/region for ordering
3152
// undefined = uses the system default, numeric = regular number sorting, base = ignore case & accents
32-
return entries.sort((a, b) =>
33-
a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
53+
return entries.sort((a, b) =>
54+
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" })
3455
);
3556
}
3657

3758

59+
// print entries either one per line (-1 flag)
60+
function printEntries(entries) {
61+
entries.forEach(entry => console.log(entry));
62+
}
63+
64+
3865
async function newLs(directory, oneFlag, allFlag) {
3966
try {
40-
// check if the path exists
67+
// check if path exists and determine if file or directory
4168
const stats = await fs.stat(directory);
42-
43-
// if it’s a file, just print its name
69+
70+
// if a file, just print the name
4471
if (stats.isFile()) {
4572
console.log(directory);
4673
return;
47-
}
74+
}
4875

49-
// read directory contents
76+
// reads directory contents
5077
const entries = await fs.readdir(directory);
5178

52-
let finalEntries;
53-
54-
// organize -a output (visible files (doesn't start with a .) and hidden files (starts with a .))
55-
const visibleFiles = entries.filter(name => !name.startsWith('.'));
56-
const hiddenFiles = entries.filter(name => name.startsWith('.') && name !== '.' && name !== '..');
57-
58-
if (allFlag) {
59-
// add visible and hidden files to the new ['.', '..'] array literal
60-
finalEntries = ['.', '..']
61-
.concat(sortEntries(visibleFiles))
62-
.concat(sortEntries(hiddenFiles));
63-
} else {
64-
// return sorted array with visible files
65-
finalEntries = sortEntries(visibleFiles);
66-
}
67-
68-
// organize -1 output
69-
if (oneFlag) {
70-
for (const entry of finalEntries) {
71-
console.log(entry);
72-
}
73-
} else {
74-
//no flags (separated by 2 spaces)
75-
console.log(finalEntries.join(' '));
76-
}
79+
// Filter out hidden files if no -a flag
80+
const filteredEntries = filterFiles(entries, allFlag);
7781

82+
// Sort the entries using the sortEntries helper
83+
const sortedEntries = sortEntries(filteredEntries);
84+
85+
// print entries for -1 flag (one per line)
86+
printEntries(sortedEntries);
7887
} catch (err) {
79-
console.error(`ls: cannot access '${directory}': No such file or directory`);
88+
console.error(`ls: cannot access '${directory}': ${err.message}`);
8089
}
81-
}
82-
83-
84-
85-
86-
87-
90+
}

implement-shell-tools/package-lock.json

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

implement-shell-tools/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "implement-shell-tools",
3+
"version": "1.0.0",
4+
"description": "Task is to implement own versions of shell tools 'cat', 'ls' and 'wc'.",
5+
"type": "module",
6+
"scripts": {
7+
"cat": "node cat/cat.js",
8+
"ls": "node ls/ls.js",
9+
"wc": "node wc/wc.js"
10+
},
11+
"dependencies": {
12+
"commander": "^14.0.2"
13+
}
14+
}

implement-shell-tools/wc/wc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ program
2323

2424
//helper function to format string for output
2525
function formatCount(count) {
26-
return count.toString().padStart(3);
26+
const paddingStart = 3
27+
return count.toString().padStart(paddingStart);
2728
}
2829

2930

@@ -52,7 +53,6 @@ async function newWc(files, options) {
5253
let totalBytes = 0;
5354

5455
for (const file of files) {
55-
5656
try {
5757
// read each file into a single text string
5858
const content = await fs.readFile(file, "utf8");

0 commit comments

Comments
 (0)