diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..c1638d83a --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,38 @@ +import { program } from "commander"; +import process from "node:process"; +import { promises as fs } from "node:fs"; + +let showNumber = false; + +program + .name("cat") + .description("Prints the output of a file to the console") + .option("-n, --number", "Displays the lines along with their number") + .argument("", "The file path") + .allowExcessArguments(); +program.parse(); + +const argv = program.args; +if (argv.length < 1) { + console.error( + `Expected exactly 1 or more arguments (paths) to be passed but got ${argv.length}.`, + ); + process.exit(1); +} + +showNumber = program.opts().number; + +const stringArr = []; +for (const path of argv) { + stringArr.push(await fs.readFile(path, "utf-8")); +} + +const flatArr = stringArr.flatMap((l) => + l + .split("\n") + .map((l, i, a) => (i < a.length - 1 ? l + "\n" : l)) + .filter((l) => l !== ""), +); + +if (!showNumber) console.log(flatArr.join("")); +else console.log(flatArr.map((l, i) => `${i + 1} ${l}`).join("")); diff --git a/implement-shell-tools/cat/package-lock.json b/implement-shell-tools/cat/package-lock.json new file mode 100644 index 000000000..737542646 --- /dev/null +++ b/implement-shell-tools/cat/package-lock.json @@ -0,0 +1,25 @@ +{ + "name": "cat", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cat", + "version": "1.0.0", + "license": "ISC", + "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==", + "license": "MIT", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/cat/package.json b/implement-shell-tools/cat/package.json new file mode 100644 index 000000000..d6423e6f3 --- /dev/null +++ b/implement-shell-tools/cat/package.json @@ -0,0 +1,15 @@ +{ + "name": "cat", + "version": "1.0.0", + "description": "You should already be familiar with the `cat` command line tool.", + "license": "ISC", + "author": "", + "type": "module", + "main": "cat.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "commander": "^14.0.3" + } +} diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..5e915ec19 --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,36 @@ +import { program } from "commander"; +import process from "node:process"; +import fs from "node:fs"; + +let vertical = false; +let showHidden = false; + +program + .name("list-directory-contents") + .description("Shows all files and folders in a directory") + .option("-1", "List one file/directory per line") + .option("-a, --all", "Show hidden files") + .argument("[path]", "Path of the directory to list (defaults to .)"); + +program.parse(); + +const argv = program.args; + +vertical = program.opts()[1]; +showHidden = program.opts().all; + +const folderPath = argv[0] || "."; + +const contents = fs.readdirSync(folderPath); + +const filtered = contents.filter((f) => f[0] !== "."); + +const dirArr = !showHidden ? filtered : contents; + +if (!vertical) { + console.log(dirArr.join(" ")); +} else { + for (const content of dirArr) { + console.log(content); + } +} diff --git a/implement-shell-tools/ls/package-lock.json b/implement-shell-tools/ls/package-lock.json new file mode 100644 index 000000000..74bc88d5b --- /dev/null +++ b/implement-shell-tools/ls/package-lock.json @@ -0,0 +1,25 @@ +{ + "name": "ls", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ls", + "version": "1.0.0", + "license": "ISC", + "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==", + "license": "MIT", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/ls/package.json b/implement-shell-tools/ls/package.json new file mode 100644 index 000000000..43e0a50d8 --- /dev/null +++ b/implement-shell-tools/ls/package.json @@ -0,0 +1,16 @@ +{ + "name": "ls", + "version": "1.0.0", + "description": "You should already be familiar with the `ls` command line tool.", + "main": "ls.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "commander": "^14.0.3" + } +} diff --git a/implement-shell-tools/wc/package-lock.json b/implement-shell-tools/wc/package-lock.json new file mode 100644 index 000000000..8aa122ddd --- /dev/null +++ b/implement-shell-tools/wc/package-lock.json @@ -0,0 +1,25 @@ +{ + "name": "wc", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wc", + "version": "1.0.0", + "license": "ISC", + "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==", + "license": "MIT", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/wc/package.json b/implement-shell-tools/wc/package.json new file mode 100644 index 000000000..dc9367c92 --- /dev/null +++ b/implement-shell-tools/wc/package.json @@ -0,0 +1,16 @@ +{ + "name": "wc", + "version": "1.0.0", + "description": "You should already be familiar with the `wc` command line tool.", + "main": "wc.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "commander": "^14.0.3" + } +} diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 000000000..b15a673bd --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,72 @@ +import { program } from "commander"; +import process from "node:process"; +import { promises as fs } from "node:fs"; + +let showLineCount = true; +let showWordCount = true; +let showByteCount = true; + +program + .name("wc") + .description( + "Print the number of lines, word and bytes for each file and a total if there are multiple files", + ) + .option("-l, --lines", "Print the number of lines") + .option("-w, --words", "Print the number of words") + .option("-c, --bytes", "Print the number of bytes") + .argument("", "The file path") + .allowExcessArguments(); +program.parse(); + +const argv = program.args; + +if (Object.keys(program.opts()).length >= 1) { + showLineCount = program.opts().lines; + showWordCount = program.opts().words; + showByteCount = program.opts().bytes; +} + +const stringArr = []; +for (const path of argv) { + stringArr.push(await fs.readFile(path, "utf-8")); +} + +let lines = 0; +let words = 0; +let bytes = 0; + +const infoArr = []; +for (let i = 0; i < stringArr.length; i++) { + const arr = []; + const line = stringArr[i].split("\n").length; + const wc = stringArr[i].split(" ").flatMap((l) => + l + .split("\n") + .map((l, i, a) => (i < a.length - 1 ? l + "\n" : l)) + .filter((l) => l.trim() !== ""), + ).length; + const lineByte = new Blob([stringArr[i]]).size; + if (showLineCount) { + arr.push(line - 1); + lines += line - 1; + } + if (showWordCount) { + arr.push(wc); + words += wc; + } + if (showByteCount) { + arr.push(lineByte); + bytes += lineByte; + } + arr.push(argv[i]); + infoArr.push(arr.join(" ")); +} + +if (infoArr.length > 1) { + const totalArr = ["total"]; + if (showByteCount) totalArr.unshift(bytes); + if (showWordCount) totalArr.unshift(words); + if (showLineCount) totalArr.unshift(lines); + infoArr.push(totalArr.join(" ")); +} +console.log(infoArr.join("\n"));