From 7e7ebfd09695d857447a3d8feb1595a0e296858c Mon Sep 17 00:00:00 2001 From: JanefrancessC Date: Sat, 28 Mar 2026 02:53:13 +0000 Subject: [PATCH 1/3] cat done --- implement-shell-tools/.gitignore | 1 + implement-shell-tools/cat/customCat.js | 42 ++++++++++++++++++++++++++ implement-shell-tools/package.json | 3 ++ 3 files changed, 46 insertions(+) create mode 100644 implement-shell-tools/.gitignore create mode 100755 implement-shell-tools/cat/customCat.js create mode 100644 implement-shell-tools/package.json diff --git a/implement-shell-tools/.gitignore b/implement-shell-tools/.gitignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/implement-shell-tools/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/implement-shell-tools/cat/customCat.js b/implement-shell-tools/cat/customCat.js new file mode 100755 index 000000000..b58d5a214 --- /dev/null +++ b/implement-shell-tools/cat/customCat.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +import { program } from "commander"; +import { promises as fs } from "node:fs"; +import process from "node:process"; + +program + .name("customCat") + .description("CLI command to concatenate and print files") + .option("-n, --number", "Number all output lines starting, at 1") + .option("-b, --nonBlank", "Number only non-blank lines, starting at 1") + .argument("", "Files to read"); + +program.parse(); + +const argv = program.args; +const options = program.opts(); + +let lineNumber = 1; + +for (const filePath of argv) { + try { + const content = await fs.readFile(filePath, "utf-8"); + const lines = content.split("\n"); + + if (lines[lines.length - 1] === "") lines.pop(); + + lines.forEach((line, index) => { + if (options.nonBlank) { + if (line.trim() !== "") { + process.stdout.write(`${(index + 1).toString().padStart(6)} ${line}\n`); + lineNumber++; + } else process.stdout.write(`${line}\n`); + } else if (options.number) { + process.stdout.write(`${(index + 1).toString().padStart(6)} ${line}\n`); + } else { + process.stdout.write(`${line}\n`); + } + }); + } catch (error) { + console.error(`customCat: ${filePath}: No such file or directory.`); + } +} diff --git a/implement-shell-tools/package.json b/implement-shell-tools/package.json new file mode 100644 index 000000000..3dbc1ca59 --- /dev/null +++ b/implement-shell-tools/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} From 30170c8f1292c6683af6a3aa88d08e2f815f1bd0 Mon Sep 17 00:00:00 2001 From: JanefrancessC Date: Mon, 30 Mar 2026 21:06:44 +0100 Subject: [PATCH 2/3] feat: implement custom ls with -1 and -a support --- implement-shell-tools/cat/customCat.js | 7 ++-- implement-shell-tools/ls/customLs.js | 53 +++++++++++++++++++++++++ implement-shell-tools/package-lock.json | 20 ++++++++++ implement-shell-tools/package.json | 5 ++- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100755 implement-shell-tools/ls/customLs.js create mode 100644 implement-shell-tools/package-lock.json diff --git a/implement-shell-tools/cat/customCat.js b/implement-shell-tools/cat/customCat.js index b58d5a214..3f0698cb5 100755 --- a/implement-shell-tools/cat/customCat.js +++ b/implement-shell-tools/cat/customCat.js @@ -15,8 +15,6 @@ program.parse(); const argv = program.args; const options = program.opts(); -let lineNumber = 1; - for (const filePath of argv) { try { const content = await fs.readFile(filePath, "utf-8"); @@ -27,8 +25,9 @@ for (const filePath of argv) { lines.forEach((line, index) => { if (options.nonBlank) { if (line.trim() !== "") { - process.stdout.write(`${(index + 1).toString().padStart(6)} ${line}\n`); - lineNumber++; + process.stdout.write( + `${(index + 1).toString().padStart(6)} ${line}\n`, + ); } else process.stdout.write(`${line}\n`); } else if (options.number) { process.stdout.write(`${(index + 1).toString().padStart(6)} ${line}\n`); diff --git a/implement-shell-tools/ls/customLs.js b/implement-shell-tools/ls/customLs.js new file mode 100755 index 000000000..c17e77b12 --- /dev/null +++ b/implement-shell-tools/ls/customLs.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +import process from "node:process"; +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("customLs") + .description("List contents of a directory") + .option("-1", "Force output to be one entry per line") + .option("-a", "Include hidden files") + .argument("[path...]", "directories to list"); + +program.parse(); + +const options = program.opts(); +const targetPaths = program.args.length > 0 ? program.args : ["."]; + +async function listDir(dirPath, showHeader) { + try { + let contents = await fs.readdir(dirPath); + + if (options.a) { + contents.push(".", ".."); + } else { + contents = contents.filter((name) => !name.startsWith(".")); + } + + contents.sort(); + + if (showHeader) { + process.stdout.write(`${dirPath}:\n`); + } + + if (options["1"]) { + contents.forEach((item) => process.stdout.write(`${item}\n`)); + } else { + process.stdout.write(`${contents.join(" ")}\n`); + } + } catch (error) { + console.error(`customLs: ${dirPath}: ${error.message}`); + process.exit(1); + } +} + +for (let i = 0; i < targetPaths.length; i++) { + const path = targetPaths[i]; + const isMultiplePath = targetPaths.length > 1; + + await listDir(path, isMultiplePath); + + if (isMultiplePath && i < targetPaths.length - 1) process.stdout.write(`\n`); +} diff --git a/implement-shell-tools/package-lock.json b/implement-shell-tools/package-lock.json new file mode 100644 index 000000000..cdf736677 --- /dev/null +++ b/implement-shell-tools/package-lock.json @@ -0,0 +1,20 @@ +{ + "name": "implement-shell-tools", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^14.0.3" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/package.json b/implement-shell-tools/package.json index 3dbc1ca59..043047a15 100644 --- a/implement-shell-tools/package.json +++ b/implement-shell-tools/package.json @@ -1,3 +1,6 @@ { - "type": "module" + "type": "module", + "dependencies": { + "commander": "^14.0.3" + } } From 5a74b431a09f5a0c5eb19fbd17a2e88528baf831 Mon Sep 17 00:00:00 2001 From: JanefrancessC Date: Wed, 1 Apr 2026 16:35:28 +0100 Subject: [PATCH 3/3] feat: implement custom wc --- .gitignore | 1 + implement-shell-tools/cat/customCat.js | 4 +- implement-shell-tools/ls/customLs.js | 4 +- implement-shell-tools/wc/customWc.js | 69 ++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100755 implement-shell-tools/wc/customWc.js diff --git a/.gitignore b/.gitignore index 3c3629e64..0099691d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +demo* diff --git a/implement-shell-tools/cat/customCat.js b/implement-shell-tools/cat/customCat.js index 3f0698cb5..98770c107 100755 --- a/implement-shell-tools/cat/customCat.js +++ b/implement-shell-tools/cat/customCat.js @@ -4,7 +4,7 @@ import { promises as fs } from "node:fs"; import process from "node:process"; program - .name("customCat") + .name("ccat") .description("CLI command to concatenate and print files") .option("-n, --number", "Number all output lines starting, at 1") .option("-b, --nonBlank", "Number only non-blank lines, starting at 1") @@ -36,6 +36,6 @@ for (const filePath of argv) { } }); } catch (error) { - console.error(`customCat: ${filePath}: No such file or directory.`); + console.error(`ccat: ${filePath}: No such file or directory.`); } } diff --git a/implement-shell-tools/ls/customLs.js b/implement-shell-tools/ls/customLs.js index c17e77b12..928566ef4 100755 --- a/implement-shell-tools/ls/customLs.js +++ b/implement-shell-tools/ls/customLs.js @@ -5,7 +5,7 @@ import { program } from "commander"; import { promises as fs } from "node:fs"; program - .name("customLs") + .name("cls") .description("List contents of a directory") .option("-1", "Force output to be one entry per line") .option("-a", "Include hidden files") @@ -38,7 +38,7 @@ async function listDir(dirPath, showHeader) { process.stdout.write(`${contents.join(" ")}\n`); } } catch (error) { - console.error(`customLs: ${dirPath}: ${error.message}`); + console.error(`cls: ${dirPath}: ${error.message}`); process.exit(1); } } diff --git a/implement-shell-tools/wc/customWc.js b/implement-shell-tools/wc/customWc.js new file mode 100755 index 000000000..0281db95e --- /dev/null +++ b/implement-shell-tools/wc/customWc.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +import { program } from "commander"; +import process from "node:process"; +import { promises as fs } from "node:fs"; + +program + .name("cwc") + .description("Displays number of lines, words, and bytes in a file") + .option("-l, --lines", "Counts number of newline characters") + .option( + "-w, --words", + "Counts sequence of characters separated by whitespace", + ) + .option("-c, --bytes", "Counts raw size of the files in bytes") + .argument("", "File(s) to read and count"); + +program.parse(); + +const options = program.opts(); +const files = program.args; + +const noFlags = !options.lines && !options.words && !options.bytes; + +let totalLines = 0; +let totalWords = 0; +let totalBytes = 0; + +async function countFiles(file) { + try { + const buffer = await fs.readFile(file); + const content = buffer.toString("utf-8"); + + const lineCount = content === "" ? 0 : content.split("\n").length - 1; + const wordCount = content.trim() ? content.trim().split(/\s+/).length : 0; + const byteCount = buffer.length; + + totalLines += lineCount; + totalWords += wordCount; + totalBytes += byteCount; + + let result = ""; + + if (options.lines || noFlags) result += `${String(lineCount).padStart(8)}`; + if (options.words || noFlags) result += `${String(wordCount).padStart(8)}`; + if (options.bytes || noFlags) result += `${String(byteCount).padStart(8)}`; + + process.stdout.write(`${result} ${file}\n`); + } catch (error) { + console.error(`cwc: ${file}: ${error.message}`); + process.exit(1); + } +} + +(async () => { + for (const file of files) { + await countFiles(file); + } + + if (files.length > 1) { + let total = ""; + + if (options.lines || noFlags) total += `${String(totalLines).padStart(8)}`; + if (options.words || noFlags) total += `${String(totalWords).padStart(8)}`; + if (options.bytes || noFlags) total += `${String(totalBytes).padStart(8)}`; + + process.stdout.write(`${total} total\n`); + } +})();