From 0482554cc675ebc268deac0b0d62358d61a35411 Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 29 Jul 2025 18:05:08 +0100 Subject: [PATCH 1/8] add package.json --- implement-shell-tools/package-lock.json | 25 +++++++++++++++++++++++++ implement-shell-tools/package.json | 16 ++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 implement-shell-tools/package-lock.json create mode 100644 implement-shell-tools/package.json diff --git a/implement-shell-tools/package-lock.json b/implement-shell-tools/package-lock.json new file mode 100644 index 00000000..35bb20ea --- /dev/null +++ b/implement-shell-tools/package-lock.json @@ -0,0 +1,25 @@ +{ + "name": "implement-shell-tools", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "implement-shell-tools", + "version": "1.0.0", + "license": "ISC", + "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..809da8b9 --- /dev/null +++ b/implement-shell-tools/package.json @@ -0,0 +1,16 @@ +{ + "name": "implement-shell-tools", + "version": "1.0.0", + "description": "Your task is to re-implement shell tools you have used.", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "commander": "^14.0.0" + } +} From 802036521fffd9010e59a3c0fc5d8bc96f9b6aed Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 29 Jul 2025 18:05:57 +0100 Subject: [PATCH 2/8] implement cat --- implement-shell-tools/cat/cat.mjs | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 implement-shell-tools/cat/cat.mjs diff --git a/implement-shell-tools/cat/cat.mjs b/implement-shell-tools/cat/cat.mjs new file mode 100644 index 00000000..c0adc546 --- /dev/null +++ b/implement-shell-tools/cat/cat.mjs @@ -0,0 +1,45 @@ +import { program } from "commander"; +import {promises as fs} from "node:fs"; + +program + .name("cat") + .description("read, display, and concatenate text files.") + .option("-n", " Number all output lines.") + .option("-b", " Number non-blank output lines.") + .arguments(""); // allow more file paths + +program.parse(); + +const options = program.opts(); +const paths = program.args; + +for(const path of paths){ + let content; + try { + content = await fs.readFile(path, "utf-8") + } catch(err) { + console.error(`Error reading file "${path}": ${err.message} `); + continue; + } + + // split file into lines + let lines = content.replace(/\n$/, "").split("\n"); + + let lineNum = 1; + + for (const line of lines){ + if(options.b){ + if(line.trim() !== ""){ + console.log(`${lineNum.toString().padStart(5)} ${line}`) + lineNum++; + } else { + console.log(""); + } + } else if(options.n){ + console.log(`${lineNum.toString().padStart(5)} ${line}`) + lineNum++; + } else{ + console.log(`${line}`) + } +} +} \ No newline at end of file From 85bb502cc8bfceb8b22e647317e656ccdb0e71bd Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 29 Jul 2025 18:06:08 +0100 Subject: [PATCH 3/8] implement ls --- implement-shell-tools/ls/ls.mjs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 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..7c8b087a --- /dev/null +++ b/implement-shell-tools/ls/ls.mjs @@ -0,0 +1,29 @@ +import {program} from "commander"; +import {promises as fs} from "node:fs"; + +program + .name("ls") + .description("List all the files in a directory") + .option("-a, --all", "Include hidden files") + .option("-1", "One entry per line") + .argument("[dir]", "directory to list", "."); + +program.parse(); + +const options = program.opts(); +const dir = program.args[0] || "."; + +const entries = await fs.readdir(dir, { withFileTypes: true }); + +const visibleNames = []; + +for(const entry of entries){ + if(!options.all && entry.name.startsWith(".")) continue; + visibleNames.push(entry.name); +} + +if(options["1"]){ + console.log(visibleNames.join("\n")); +} else{ + console.log(visibleNames.join(" ")); +} From e785ce0b5987cb0f5a12883a1452eb1d40c36528 Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 29 Jul 2025 18:06:22 +0100 Subject: [PATCH 4/8] implement wc --- implement-shell-tools/wc/wc.mjs | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 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..595a1f69 --- /dev/null +++ b/implement-shell-tools/wc/wc.mjs @@ -0,0 +1,65 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("wc") + .description("Display numbers of line, words, and bytes in each file") + .option("-l", "Number of lines") + .option("-w", "Number of words") + .option("-c", "Number of bytes") + .argument(""); + +program.parse(); + +const options = program.opts(); +const paths = program.args; + +let totalLines = 0; +let totalWords = 0; +let totalBytes = 0; + +for(const path of paths){ + let content; + try{ + content = await fs.readFile(path, "utf-8"); + } catch (err){ + console.error(`Error reading file "${path}":`, err.message); + continue; + } + + const lines = content.replace(/\n$/, "").split("\n"); + + const words = content.trim().split(/\s+/); // handles multiple spaces + const { size } = await fs.stat(path); + + const lineCount = lines.length; + const wordCount = words.length; + const byteCount = size; + + totalLines += lineCount; + totalWords += wordCount; + totalBytes += byteCount; + + if(options.l) { + console.log(`\t${lineCount} ${path}`); + } else if(options.w) { + console.log(`\t${wordCount} ${path}`); + } else if(options.c) { + console.log(`\t${byteCount} ${path}`) + } else { + console.log(`\t${lineCount}\t${wordCount}\t${size} ${path}`); + } + +} + +if (paths.length > 1) { + if (options.l) { + console.log(`\t${totalLines} total`); + } else if (options.w) { + console.log(`\t${totalWords} total`); + } else if (options.c) { + console.log(`\t${totalBytes} total`); + } else { + console.log(`\t${totalLines}\t${totalWords}\t${totalBytes} total`); + } +} \ No newline at end of file From 9139868f94aab102d2d7db2f1e24b5592d67b57c Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Wed, 6 Aug 2025 17:10:36 +0100 Subject: [PATCH 5/8] fix cat error handler --- implement-shell-tools/cat/cat.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/cat/cat.mjs b/implement-shell-tools/cat/cat.mjs index c0adc546..0b43572d 100644 --- a/implement-shell-tools/cat/cat.mjs +++ b/implement-shell-tools/cat/cat.mjs @@ -13,18 +13,19 @@ program.parse(); const options = program.opts(); const paths = program.args; +let hadError = false; for(const path of paths){ let content; try { content = await fs.readFile(path, "utf-8") } catch(err) { console.error(`Error reading file "${path}": ${err.message} `); + hadError = true; continue; } // split file into lines let lines = content.replace(/\n$/, "").split("\n"); - let lineNum = 1; for (const line of lines){ @@ -42,4 +43,6 @@ for(const path of paths){ console.log(`${line}`) } } -} \ No newline at end of file +} + +if (hadError) process.exit(1); \ No newline at end of file From 287fd3ae883042ccc6e4f1e8f3b7ec3175e83552 Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Wed, 6 Aug 2025 17:19:37 +0100 Subject: [PATCH 6/8] add try/catch for ls --- implement-shell-tools/ls/ls.mjs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/implement-shell-tools/ls/ls.mjs b/implement-shell-tools/ls/ls.mjs index 7c8b087a..b445025b 100644 --- a/implement-shell-tools/ls/ls.mjs +++ b/implement-shell-tools/ls/ls.mjs @@ -5,15 +5,21 @@ program .name("ls") .description("List all the files in a directory") .option("-a, --all", "Include hidden files") - .option("-1", "One entry per line") - .argument("[dir]", "directory to list", "."); + .option("-1, --one", "One entry per line") + .argument("[dir]", "directory to list"); program.parse(); const options = program.opts(); const dir = program.args[0] || "."; -const entries = await fs.readdir(dir, { withFileTypes: true }); +let entries; +try { + entries = await fs.readdir(dir, { withFileTypes: true }); +} catch (err) { + console.error(`Error accessing ${dir}: ${err.message}`); + process.exit(1); +} const visibleNames = []; @@ -22,8 +28,11 @@ for(const entry of entries){ visibleNames.push(entry.name); } -if(options["1"]){ +if (options.all) { + visibleNames.unshift(".", ".."); +} +if(options.one){ console.log(visibleNames.join("\n")); } else{ console.log(visibleNames.join(" ")); -} +} \ No newline at end of file From d8c098ff9c5c3c5748dd84353e8100b37d566d1a Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 26 Aug 2025 16:45:51 +0100 Subject: [PATCH 7/8] fix cat --- implement-shell-tools/cat/cat.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/cat/cat.mjs b/implement-shell-tools/cat/cat.mjs index 0b43572d..660cadc3 100644 --- a/implement-shell-tools/cat/cat.mjs +++ b/implement-shell-tools/cat/cat.mjs @@ -13,6 +13,9 @@ program.parse(); const options = program.opts(); const paths = program.args; +const numNonBlank = options.b; +const numAll = !!options.n && !numNonBlank; + let hadError = false; for(const path of paths){ let content; @@ -29,14 +32,14 @@ for(const path of paths){ let lineNum = 1; for (const line of lines){ - if(options.b){ + if(numNonBlank){ if(line.trim() !== ""){ console.log(`${lineNum.toString().padStart(5)} ${line}`) lineNum++; } else { console.log(""); } - } else if(options.n){ + } else if(numAll){ console.log(`${lineNum.toString().padStart(5)} ${line}`) lineNum++; } else{ From f6c367c2e3d97f9af9f4d41e2e90c6c86c88e81f Mon Sep 17 00:00:00 2001 From: shaden-pr Date: Tue, 26 Aug 2025 16:49:48 +0100 Subject: [PATCH 8/8] Exit with code 1 if wc fails to read a file --- implement-shell-tools/wc/wc.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/implement-shell-tools/wc/wc.mjs b/implement-shell-tools/wc/wc.mjs index 595a1f69..ad54e55f 100644 --- a/implement-shell-tools/wc/wc.mjs +++ b/implement-shell-tools/wc/wc.mjs @@ -17,6 +17,7 @@ const paths = program.args; let totalLines = 0; let totalWords = 0; let totalBytes = 0; +let hadError = false; for(const path of paths){ let content; @@ -24,6 +25,7 @@ for(const path of paths){ content = await fs.readFile(path, "utf-8"); } catch (err){ console.error(`Error reading file "${path}":`, err.message); + hadError = true; continue; } @@ -62,4 +64,6 @@ if (paths.length > 1) { } else { console.log(`\t${totalLines}\t${totalWords}\t${totalBytes} total`); } -} \ No newline at end of file +} + +if (hadError) process.exit(1); \ No newline at end of file