diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..27989f877 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,49 @@ +import process from "node:process"; +import { promises as fs } from "node:fs"; +import { program } from "commander"; + +program + .name("cat") + .description("Concatenate and print files") + .option("-n, --number", "Number all lines", false) + .option("-b, --number-nonblank", "Number non-empty lines", false) + .argument("", "The file(s) to read"); + +program.parse(); + +const paths = program.args; +if (paths.length === 0) { + console.error("Please provide at least one file"); + process.exit(1); +} + +const options = program.opts(); +const showLineNumbers = options.number; +const numberNonEmpty = options.numberNonblank; + +let lineNumber = 1; + +for (const path of paths) { + try { + const content = await fs.readFile(path, "utf-8"); + const lines = content.split("\n"); + + for (const line of lines) { + if (numberNonEmpty) { + if (line.trim() !== "") { + console.log(`${lineNumber} ${line}`); + lineNumber++; + } else { + console.log(""); + } + } else if (showLineNumbers) { + console.log(`${lineNumber} ${line}`); + lineNumber++; + } else { + console.log(line); + } + } + } catch (err) { + console.error(`Error reading file: ${path}`); + } +} 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..5d63dc09d --- /dev/null +++ b/implement-shell-tools/cat/package.json @@ -0,0 +1,16 @@ +{ + "name": "cat", + "version": "1.0.0", + "description": "You should already be familiar with the `cat` command line tool.", + "main": "cat.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/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..0b41753ab --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,40 @@ +import process from "node:process"; +import { promises as fs } from "node:fs"; +import { program } from "commander"; + +program + .name("ls") + .description("List directory contents") + .option("-1", "List one file per line") + .option("-a, --all", "Include hidden files", false) + .argument("[path]", "Directory path", "."); + +program.parse(); + +const path = program.args[0] || "."; +const options = program.opts(); + +const showAll = options.all; + +try { + const stat = await fs.stat(path); + + if (stat.isDirectory()) { + const files = await fs.readdir(path); + + let filteredFiles = files; + + if (!showAll) { + filteredFiles = files.filter((file) => !file.startsWith(".")); + } + + for (const file of filteredFiles) { + console.log(file); + } + } else { + console.log(path); + } +} catch (err) { + console.error(`Error: cannot access ${path}`); + process.exit(1); +} 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..357f2a61f --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,60 @@ +import process from "node:process"; +import { promises as fs } from "node:fs"; +import { program } from "commander"; + +program + .name("wc") + .description("Word, line, and byte count") + .option("-l, --lines", "Count lines") + .option("-w, --words", "Count words") + .option("-c, --bytes", "Count bytes") + .argument(" 1) { + printResult(totalLines, totalWords, totalBytes, "total", options); +} + +function printResult(lines, words, bytes, label, options) { + 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}`); + } +}