From 05921ccb537802c21ed58b43af0936d803f233c3 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sat, 26 Jul 2025 14:59:18 +0100 Subject: [PATCH 01/18] implement cat without flag --- implement-shell-tools/cat/cat.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 implement-shell-tools/cat/cat.js diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 00000000..54cb5ed5 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,11 @@ +import process from "node:process"; +import { promises as fs } from "node:fs"; + +const argv = process.argv.slice(2); +if (argv.length != 1) { + console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`); + process.exit(1); +} +const path = argv[0]; +const content = await fs.readFile(path, "utf-8"); +process.stdout.write(content); \ No newline at end of file From 3c024ff2f68c5ce4d2463827db87f0361ef2761d Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sat, 26 Jul 2025 15:25:58 +0100 Subject: [PATCH 02/18] refactor to use commander --- implement-shell-tools/cat/cat.js | 9 ++++++++- implement-shell-tools/package-lock.json | 21 +++++++++++++++++++++ implement-shell-tools/package.json | 6 ++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 implement-shell-tools/package-lock.json create mode 100644 implement-shell-tools/package.json diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index 54cb5ed5..adfca0cd 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -1,7 +1,14 @@ +import { program } from "commander"; import process from "node:process"; import { promises as fs } from "node:fs"; -const argv = process.argv.slice(2); +program + .name("cat") + .description("print the content of file") + .argument("", "The file path to process"); +program.parse(); + +const argv = program.args; if (argv.length != 1) { console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`); process.exit(1); diff --git a/implement-shell-tools/package-lock.json b/implement-shell-tools/package-lock.json new file mode 100644 index 00000000..07c7f3f2 --- /dev/null +++ b/implement-shell-tools/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "implement-shell-tools", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^14.0.0" + } + }, + "node_modules/commander": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "license": "MIT", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/package.json b/implement-shell-tools/package.json new file mode 100644 index 00000000..68279600 --- /dev/null +++ b/implement-shell-tools/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "commander": "^14.0.0" + } +} From d2a21b92798047e46ccfdb5f4baefa0b1fffbc66 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sun, 27 Jul 2025 13:40:42 +0100 Subject: [PATCH 03/18] update code to number the output line --- implement-shell-tools/cat/cat.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index adfca0cd..fa3a06ec 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -5,6 +5,7 @@ import { promises as fs } from "node:fs"; program .name("cat") .description("print the content of file") + .option("-n , --line-numbers","Number the output lines, starting at 1") .argument("", "The file path to process"); program.parse(); @@ -13,6 +14,17 @@ if (argv.length != 1) { console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`); process.exit(1); } + +const options = program.opts(); const path = argv[0]; const content = await fs.readFile(path, "utf-8"); -process.stdout.write(content); \ No newline at end of file +if (options.lineNumbers) { + const lines= content.split(/\r?\n/); + lines.forEach((line, index) => { + console.log(index+1, line) + }); + +} else { + + process.stdout.write(content); +} From 461f9bd0c5cb17a31646395de38a67e073fbb36b Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sun, 27 Jul 2025 14:09:24 +0100 Subject: [PATCH 04/18] code update to support multiple files. --- implement-shell-tools/cat/cat.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index fa3a06ec..d0647a6a 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -6,25 +6,33 @@ program .name("cat") .description("print the content of file") .option("-n , --line-numbers","Number the output lines, starting at 1") - .argument("", "The file path to process"); + .argument("", "The file path(s) to process"); // to support multiple file program.parse(); const argv = program.args; -if (argv.length != 1) { - console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`); +const options = program.opts(); +if (argv.length === 0) { + console.error(`No file paths provided`);// to support more files process.exit(1); } -const options = program.opts(); -const path = argv[0]; +let lineCounter = 1; + +for (const path of argv) { + try { const content = await fs.readFile(path, "utf-8"); +const lines= content.split(/\r?\n/); if (options.lineNumbers) { - const lines= content.split(/\r?\n/); - lines.forEach((line, index) => { + lines.forEach((line) => { + const lineNumber = String(lineCounter++).padStart(6, ' '); console.log(index+1, line) }); } else { - process.stdout.write(content); + if (!content.endsWith('\n')) process.stdout.write('\n'); + } + } catch (err) { + console.error(`cat: ${path}: ${err.message}`); + } } From 45fddf084e26fa68802f0950e6cbb9756d1152f8 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sun, 27 Jul 2025 14:55:41 +0100 Subject: [PATCH 05/18] update code to implement line number in multiple files. --- implement-shell-tools/cat/cat.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index d0647a6a..35985932 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -5,7 +5,7 @@ import { promises as fs } from "node:fs"; program .name("cat") .description("print the content of file") - .option("-n , --line-numbers","Number the output lines, starting at 1") + .option("-n, --line-numbers","Number the output lines, starting at 1") .argument("", "The file path(s) to process"); // to support multiple file program.parse(); @@ -22,10 +22,13 @@ for (const path of argv) { try { const content = await fs.readFile(path, "utf-8"); const lines= content.split(/\r?\n/); +if (lines.length && lines[lines.length - 1] === '') {//// Remove trailing empty line if it's just from the final newline + lines.pop(); +} if (options.lineNumbers) { lines.forEach((line) => { const lineNumber = String(lineCounter++).padStart(6, ' '); - console.log(index+1, line) + console.log(`${lineNumber}\t${line}`) }); } else { From 82d13541178090dc81fd654e3dae4247bbda92c8 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Sun, 27 Jul 2025 15:02:14 +0100 Subject: [PATCH 06/18] update for -b flag --- implement-shell-tools/cat/cat.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index 35985932..bc06cfec 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -6,6 +6,7 @@ program .name("cat") .description("print the content of file") .option("-n, --line-numbers","Number the output lines, starting at 1") + .option("-b, --number-nonblank", "Number non-empty output lines, overrides -n") .argument("", "The file path(s) to process"); // to support multiple file program.parse(); @@ -25,7 +26,18 @@ const lines= content.split(/\r?\n/); if (lines.length && lines[lines.length - 1] === '') {//// Remove trailing empty line if it's just from the final newline lines.pop(); } -if (options.lineNumbers) { + +if (options.numberNonblank) { + lines.forEach((line) => { + if (line.trim() === "") { + console.log(""); // Blank line, no number + } else { + const lineNumber = String(lineCounter++).padStart(6, " "); + console.log(`${lineNumber}\t${line}`); + } + }); + +}else if (options.lineNumbers) { lines.forEach((line) => { const lineNumber = String(lineCounter++).padStart(6, ' '); console.log(`${lineNumber}\t${line}`) From f616c9dee4a4a5eb35a14ec316f31fcd7351a322 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 11:54:16 +0100 Subject: [PATCH 07/18] implement ls command with -1 flag and default current directory --- implement-shell-tools/ls/ls.mjs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 implement-shell-tools/ls/ls.mjs diff --git a/implement-shell-tools/ls/ls.mjs b/implement-shell-tools/ls/ls.mjs new file mode 100644 index 00000000..6131bdf7 --- /dev/null +++ b/implement-shell-tools/ls/ls.mjs @@ -0,0 +1,23 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("ls") + .description("Displays files in a directory") + .option("-1, --one", "Display each file on a new line")// for listing one file per line + .argument("[filepath]", "Directory to list files from (default: current)") + +await program.parseAsync(); + +const opts = program.opts(); +const args = program.args; + +const path = args[0] || "."; //Use current directory if no path provided +let files = await fs.readdir(path); + +if (opts.one) { + files.forEach(file => console.log(file)); +} else { + console.log(files.join(" ")); +} + From f478664c352296c2a236ae5796945d2d32e81779 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 12:03:00 +0100 Subject: [PATCH 08/18] update code to support -a flag --- implement-shell-tools/ls/ls.mjs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/implement-shell-tools/ls/ls.mjs b/implement-shell-tools/ls/ls.mjs index 6131bdf7..d90b57b5 100644 --- a/implement-shell-tools/ls/ls.mjs +++ b/implement-shell-tools/ls/ls.mjs @@ -5,6 +5,7 @@ program .name("ls") .description("Displays files in a directory") .option("-1, --one", "Display each file on a new line")// for listing one file per line + .option("-a, --all", "Include hidden files") .argument("[filepath]", "Directory to list files from (default: current)") await program.parseAsync(); @@ -13,7 +14,18 @@ const opts = program.opts(); const args = program.args; const path = args[0] || "."; //Use current directory if no path provided -let files = await fs.readdir(path); +let files ; +try { + files = await fs.readdir(path); +}catch (err) { + console.error(`ls: cannot access '${path}': ${err.message}`); + process.exit(1); + +} + +if (!opts.all) { + files = files.filter(file => !file.startsWith("."));// if path not provided use current directory +} if (opts.one) { files.forEach(file => console.log(file)); From 83c24e03a5f5d01ffe5bc5773e6f1dea90d59966 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 17:13:50 +0100 Subject: [PATCH 09/18] for total line, words, bytes when multiple files passed to wc --- implement-shell-tools/wc/wc.mjs | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 implement-shell-tools/wc/wc.mjs diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs new file mode 100644 index 00000000..21e762bc --- /dev/null +++ b/implement-shell-tools/wc/wc.mjs @@ -0,0 +1,40 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("wc") + .description("Count lines, words, and bytes in files") + .argument("", "One or more files to process"); +await program.parseAsync(); + +const files = program.args; + +function countContent(content){ + const lines = (content.match(/\n/g) || []).length; + const words = content.trim().split(/\s+/).filter(Boolean).length; + const bytes = Buffer.byteLength(content, 'utf-8'); + return {lines, words, bytes}; +} + +let totalLines = 0; +let totalWords = 0; +let totalBytes = 0; + +for(const file of files){ + try{ + const content = await fs.readFile(file, "utf-8"); + const {lines, words, bytes } = countContent(content); + console.log(`${lines} ${words} ${bytes} ${file}`); + + totalLines += lines; + totalWords += words; + totalBytes += bytes; + + }catch (err) { + console.error(`wc: ${file}: ${err.message}`); + } +} + +if (files.length > 1) { + console.log(`${totalLines} ${totalWords} ${totalBytes} total`); +} \ No newline at end of file From 5679649ce3826b4a85afe983c4b72d0a5d3e8ea9 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 18:09:16 +0100 Subject: [PATCH 10/18] update code to include -l flag --- implement-shell-tools/wc/wc.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs index 21e762bc..9728b313 100644 --- a/implement-shell-tools/wc/wc.mjs +++ b/implement-shell-tools/wc/wc.mjs @@ -4,10 +4,12 @@ import { promises as fs } from "node:fs"; program .name("wc") .description("Count lines, words, and bytes in files") + .option("-l, --lines", "Only print line counts") .argument("", "One or more files to process"); await program.parseAsync(); const files = program.args; +const options = program.opts(); function countContent(content){ const lines = (content.match(/\n/g) || []).length; @@ -24,7 +26,12 @@ for(const file of files){ try{ const content = await fs.readFile(file, "utf-8"); const {lines, words, bytes } = countContent(content); - console.log(`${lines} ${words} ${bytes} ${file}`); + + if (options.lines) { + console.log(`${lines} ${file}`); + } else { + console.log(`${lines} ${words} ${bytes} ${file}`); + } totalLines += lines; totalWords += words; @@ -36,5 +43,5 @@ for(const file of files){ } if (files.length > 1) { - console.log(`${totalLines} ${totalWords} ${totalBytes} total`); + console.log(`${totalLines} total`); } \ No newline at end of file From dacfd5bc272808c8e53571e4342a97e4825ddc0f Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 18:27:17 +0100 Subject: [PATCH 11/18] update in code to support wc -l and -w fla for sample-files/3.txt --- implement-shell-tools/wc/wc.mjs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs index 9728b313..8b7e21c5 100644 --- a/implement-shell-tools/wc/wc.mjs +++ b/implement-shell-tools/wc/wc.mjs @@ -5,6 +5,7 @@ program .name("wc") .description("Count lines, words, and bytes in files") .option("-l, --lines", "Only print line counts") + .option("-w, --words", "Only print word counts") .argument("", "One or more files to process"); await program.parseAsync(); @@ -29,7 +30,9 @@ for(const file of files){ if (options.lines) { console.log(`${lines} ${file}`); - } else { + } else if (options.words) { + console.log(`${words} ${file}`); + }else { console.log(`${lines} ${words} ${bytes} ${file}`); } @@ -43,5 +46,11 @@ for(const file of files){ } if (files.length > 1) { - console.log(`${totalLines} total`); -} \ No newline at end of file + if (options.lines) { + console.log(`${totalLines} total`)//if more then 1 file is + }else if (options.words) { + console.log(`${totalWords} total`); + } else { + console.log(`${totalLines} ${totalWords} ${totalBytes} total`); + } +} From 7ad30b8162c8a88b6954bab105049a61a047e28b Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Tue, 29 Jul 2025 18:32:56 +0100 Subject: [PATCH 12/18] update in code to support -c flag --- implement-shell-tools/wc/wc.mjs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs index 8b7e21c5..068e63c8 100644 --- a/implement-shell-tools/wc/wc.mjs +++ b/implement-shell-tools/wc/wc.mjs @@ -6,6 +6,7 @@ program .description("Count lines, words, and bytes in files") .option("-l, --lines", "Only print line counts") .option("-w, --words", "Only print word counts") + .option("-c, --bytes", "Only print byte counts") .argument("", "One or more files to process"); await program.parseAsync(); @@ -32,6 +33,8 @@ for(const file of files){ console.log(`${lines} ${file}`); } else if (options.words) { console.log(`${words} ${file}`); + }else if (options.bytes) { + console.log(`${bytes} ${file}`); }else { console.log(`${lines} ${words} ${bytes} ${file}`); } @@ -49,8 +52,10 @@ if (files.length > 1) { if (options.lines) { console.log(`${totalLines} total`)//if more then 1 file is }else if (options.words) { - console.log(`${totalWords} total`); + console.log(`${totalWords} total`); + } else if (options.bytes) { + console.log(`${totalBytes} total`); } else { - console.log(`${totalLines} ${totalWords} ${totalBytes} total`); + console.log(`${totalLines} ${totalWords} ${totalBytes} total`); } } From 99a5dc1a8fe6246100511f8bcf9a7e80e36a6609 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Wed, 30 Jul 2025 20:26:46 +0100 Subject: [PATCH 13/18] change ls file name --- implement-shell-tools/ls/ls.js | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 implement-shell-tools/ls/ls.js diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 00000000..d90b57b5 --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,35 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("ls") + .description("Displays files in a directory") + .option("-1, --one", "Display each file on a new line")// for listing one file per line + .option("-a, --all", "Include hidden files") + .argument("[filepath]", "Directory to list files from (default: current)") + +await program.parseAsync(); + +const opts = program.opts(); +const args = program.args; + +const path = args[0] || "."; //Use current directory if no path provided +let files ; +try { + files = await fs.readdir(path); +}catch (err) { + console.error(`ls: cannot access '${path}': ${err.message}`); + process.exit(1); + +} + +if (!opts.all) { + files = files.filter(file => !file.startsWith("."));// if path not provided use current directory +} + +if (opts.one) { + files.forEach(file => console.log(file)); +} else { + console.log(files.join(" ")); +} + From 9e6197298e58e4307d866de591b3ec3cc27540c9 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Wed, 30 Jul 2025 20:27:58 +0100 Subject: [PATCH 14/18] wc file name change --- implement-shell-tools/wc/wc.js | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 implement-shell-tools/wc/wc.js diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 00000000..068e63c8 --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,61 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("wc") + .description("Count lines, words, and bytes in files") + .option("-l, --lines", "Only print line counts") + .option("-w, --words", "Only print word counts") + .option("-c, --bytes", "Only print byte counts") + .argument("", "One or more files to process"); +await program.parseAsync(); + +const files = program.args; +const options = program.opts(); + +function countContent(content){ + const lines = (content.match(/\n/g) || []).length; + const words = content.trim().split(/\s+/).filter(Boolean).length; + const bytes = Buffer.byteLength(content, 'utf-8'); + return {lines, words, bytes}; +} + +let totalLines = 0; +let totalWords = 0; +let totalBytes = 0; + +for(const file of files){ + try{ + const content = await fs.readFile(file, "utf-8"); + const {lines, words, bytes } = countContent(content); + + if (options.lines) { + console.log(`${lines} ${file}`); + } else if (options.words) { + console.log(`${words} ${file}`); + }else if (options.bytes) { + console.log(`${bytes} ${file}`); + }else { + console.log(`${lines} ${words} ${bytes} ${file}`); + } + + totalLines += lines; + totalWords += words; + totalBytes += bytes; + + }catch (err) { + console.error(`wc: ${file}: ${err.message}`); + } +} + +if (files.length > 1) { + if (options.lines) { + console.log(`${totalLines} total`)//if more then 1 file is + }else if (options.words) { + console.log(`${totalWords} total`); + } else if (options.bytes) { + console.log(`${totalBytes} total`); + } else { + console.log(`${totalLines} ${totalWords} ${totalBytes} total`); + } +} From 159982375ffdedcba712e76fba8e849d1a5942dc Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Wed, 30 Jul 2025 20:35:15 +0100 Subject: [PATCH 15/18] Remove old wc.mjs file --- implement-shell-tools/wc/wc.mjs | 61 --------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 implement-shell-tools/wc/wc.mjs diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs deleted file mode 100644 index 068e63c8..00000000 --- a/implement-shell-tools/wc/wc.mjs +++ /dev/null @@ -1,61 +0,0 @@ -import { program } from "commander"; -import { promises as fs } from "node:fs"; - -program - .name("wc") - .description("Count lines, words, and bytes in files") - .option("-l, --lines", "Only print line counts") - .option("-w, --words", "Only print word counts") - .option("-c, --bytes", "Only print byte counts") - .argument("", "One or more files to process"); -await program.parseAsync(); - -const files = program.args; -const options = program.opts(); - -function countContent(content){ - const lines = (content.match(/\n/g) || []).length; - const words = content.trim().split(/\s+/).filter(Boolean).length; - const bytes = Buffer.byteLength(content, 'utf-8'); - return {lines, words, bytes}; -} - -let totalLines = 0; -let totalWords = 0; -let totalBytes = 0; - -for(const file of files){ - try{ - const content = await fs.readFile(file, "utf-8"); - const {lines, words, bytes } = countContent(content); - - if (options.lines) { - console.log(`${lines} ${file}`); - } else if (options.words) { - console.log(`${words} ${file}`); - }else if (options.bytes) { - console.log(`${bytes} ${file}`); - }else { - console.log(`${lines} ${words} ${bytes} ${file}`); - } - - totalLines += lines; - totalWords += words; - totalBytes += bytes; - - }catch (err) { - console.error(`wc: ${file}: ${err.message}`); - } -} - -if (files.length > 1) { - if (options.lines) { - console.log(`${totalLines} total`)//if more then 1 file is - }else if (options.words) { - console.log(`${totalWords} total`); - } else if (options.bytes) { - console.log(`${totalBytes} total`); - } else { - console.log(`${totalLines} ${totalWords} ${totalBytes} total`); - } -} From 13af0172ccb279a330643c00da9d537e3c471138 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Wed, 30 Jul 2025 20:36:33 +0100 Subject: [PATCH 16/18] Remove old ls.mjs file --- implement-shell-tools/ls/ls.mjs | 35 --------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 implement-shell-tools/ls/ls.mjs diff --git a/implement-shell-tools/ls/ls.mjs b/implement-shell-tools/ls/ls.mjs deleted file mode 100644 index d90b57b5..00000000 --- a/implement-shell-tools/ls/ls.mjs +++ /dev/null @@ -1,35 +0,0 @@ -import { program } from "commander"; -import { promises as fs } from "node:fs"; - -program - .name("ls") - .description("Displays files in a directory") - .option("-1, --one", "Display each file on a new line")// for listing one file per line - .option("-a, --all", "Include hidden files") - .argument("[filepath]", "Directory to list files from (default: current)") - -await program.parseAsync(); - -const opts = program.opts(); -const args = program.args; - -const path = args[0] || "."; //Use current directory if no path provided -let files ; -try { - files = await fs.readdir(path); -}catch (err) { - console.error(`ls: cannot access '${path}': ${err.message}`); - process.exit(1); - -} - -if (!opts.all) { - files = files.filter(file => !file.startsWith("."));// if path not provided use current directory -} - -if (opts.one) { - files.forEach(file => console.log(file)); -} else { - console.log(files.join(" ")); -} - From 165d7348b9ad09c999197c7e3f10c29c062aca91 Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Thu, 14 Aug 2025 13:25:41 +0100 Subject: [PATCH 17/18] update in code and install glob to expand *.txt --- implement-shell-tools/cat/cat.js | 79 +++++++++++----------- implement-shell-tools/package-lock.json | 88 ++++++++++++++++++++++++- implement-shell-tools/package.json | 3 +- 3 files changed, 130 insertions(+), 40 deletions(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index bc06cfec..8b417568 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -1,53 +1,56 @@ import { program } from "commander"; import process from "node:process"; import { promises as fs } from "node:fs"; +import glob from "glob"; program - .name("cat") - .description("print the content of file") - .option("-n, --line-numbers","Number the output lines, starting at 1") - .option("-b, --number-nonblank", "Number non-empty output lines, overrides -n") - .argument("", "The file path(s) to process"); // to support multiple file + .name("cat") + .description("print the content of file") + .option("-n, --line-numbers", "Number the output lines, starting at 1") + .option( + "-b, --number-nonblank", + "Number non-empty output lines, overrides -n" + ) + .argument("", "The file path(s) to process"); // to support multiple file program.parse(); -const argv = program.args; +const argv = program.args.flatMap((path) => glob.sync(path)); + const options = program.opts(); if (argv.length === 0) { - console.error(`No file paths provided`);// to support more files - process.exit(1); + console.error(`No file paths provided`); // to support more files + process.exit(1); } let lineCounter = 1; - -for (const path of argv) { - try { -const content = await fs.readFile(path, "utf-8"); -const lines= content.split(/\r?\n/); -if (lines.length && lines[lines.length - 1] === '') {//// Remove trailing empty line if it's just from the final newline - lines.pop(); +function printLine(line, number = false) { + if (number && line.trim() !== "") { + const lineNumber = String(lineCounter++).padStart(6, " "); + console.log(`${lineNumber}\t${line}`); + } else { + console.log(line); + } } - -if (options.numberNonblank) { - lines.forEach((line) => { - if (line.trim() === "") { - console.log(""); // Blank line, no number - } else { - const lineNumber = String(lineCounter++).padStart(6, " "); - console.log(`${lineNumber}\t${line}`); - } - }); - -}else if (options.lineNumbers) { - lines.forEach((line) => { - const lineNumber = String(lineCounter++).padStart(6, ' '); - console.log(`${lineNumber}\t${line}`) - }); - -} else { - process.stdout.write(content); - if (!content.endsWith('\n')) process.stdout.write('\n'); - } - } catch (err) { - console.error(`cat: ${path}: ${err.message}`); +// Helper function to print totals for multiple files +function printTotal(total, label = "total") { + console.log(`${total} ${label}`); +} +for (const path of argv) { + try { + const content = await fs.readFile(path, "utf-8"); + const lines = content.split(/\r?\n/); + if (lines.length && lines[lines.length - 1] === "") { + // Remove trailing empty line if it's just from the final newline + lines.pop(); } + // Determine if we need to number lines + const numberLines = options.numberNonblank || options.lineNumbers; + + lines.forEach((line) => printLine(line, numberLines)); + + // Add a newline if the file didn't end with one + if (!content.endsWith("\n")) process.stdout.write("\n"); + } catch (err) { + console.error(`cat: ${path}: ${err.message}`); + } } diff --git a/implement-shell-tools/package-lock.json b/implement-shell-tools/package-lock.json index 07c7f3f2..520e1d48 100644 --- a/implement-shell-tools/package-lock.json +++ b/implement-shell-tools/package-lock.json @@ -5,7 +5,23 @@ "packages": { "": { "dependencies": { - "commander": "^14.0.0" + "commander": "^14.0.0", + "glob": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/commander": { @@ -16,6 +32,76 @@ "engines": { "node": ">=20" } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" } } } diff --git a/implement-shell-tools/package.json b/implement-shell-tools/package.json index 68279600..f7d3ca5a 100644 --- a/implement-shell-tools/package.json +++ b/implement-shell-tools/package.json @@ -1,6 +1,7 @@ { "type": "module", "dependencies": { - "commander": "^14.0.0" + "commander": "^14.0.0", + "glob": "^8.1.0" } } From 4069a78d48bc86c0d7750521385d0d2815cd9a2f Mon Sep 17 00:00:00 2001 From: sheetalkharab Date: Thu, 14 Aug 2025 13:51:24 +0100 Subject: [PATCH 18/18] update code to remove duplicate code --- implement-shell-tools/wc/wc.js | 70 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js index 068e63c8..0b335d42 100644 --- a/implement-shell-tools/wc/wc.js +++ b/implement-shell-tools/wc/wc.js @@ -1,5 +1,7 @@ import { program } from "commander"; import { promises as fs } from "node:fs"; +import pkg from "glob"; +const { glob } = pkg; program .name("wc") @@ -10,52 +12,50 @@ program .argument("", "One or more files to process"); await program.parseAsync(); -const files = program.args; +const files = program.args.flatMap((path) => glob.sync(path)); const options = program.opts(); -function countContent(content){ - const lines = (content.match(/\n/g) || []).length; - const words = content.trim().split(/\s+/).filter(Boolean).length; - const bytes = Buffer.byteLength(content, 'utf-8'); - return {lines, words, bytes}; +function countContent(content) { + const lines = (content.match(/\n/g) || []).length; + const words = content.trim().split(/\s+/).filter(Boolean).length; + const bytes = Buffer.byteLength(content, "utf-8"); + return { lines, words, bytes }; +} +// Helper to print counts based on options +function printCounts({ lines, words, bytes }, label) { + if (options.lines) { + console.log(`${lines} ${label}`); + } else if (options.words) { + console.log(`${words} ${label}`); + } else if (options.bytes) { + console.log(`${bytes} ${label}`); + } else { + console.log(`${lines} ${words} ${bytes} ${label}`); + } } let totalLines = 0; let totalWords = 0; let totalBytes = 0; -for(const file of files){ - try{ - const content = await fs.readFile(file, "utf-8"); - const {lines, words, bytes } = countContent(content); - - if (options.lines) { - console.log(`${lines} ${file}`); - } else if (options.words) { - console.log(`${words} ${file}`); - }else if (options.bytes) { - console.log(`${bytes} ${file}`); - }else { - console.log(`${lines} ${words} ${bytes} ${file}`); - } - - totalLines += lines; - totalWords += words; - totalBytes += bytes; - - }catch (err) { +for (const file of files) { + try { + const content = await fs.readFile(file, "utf-8"); + const counts = countContent(content); + + printCounts(counts, file); + + totalLines += counts.lines; + totalWords += counts.words; + totalBytes += counts.bytes; + } catch (err) { console.error(`wc: ${file}: ${err.message}`); } } if (files.length > 1) { - if (options.lines) { - console.log(`${totalLines} total`)//if more then 1 file is - }else if (options.words) { - console.log(`${totalWords} total`); - } else if (options.bytes) { - console.log(`${totalBytes} total`); - } else { - console.log(`${totalLines} ${totalWords} ${totalBytes} total`); - } + printCounts( + { lines: totalLines, words: totalWords, bytes: totalBytes }, + "total" + ); }