diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..7680ac341 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,53 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("check-for-cat") + .description("Implement my own version of cat") + .argument("", "The file paths to process") + .option("-n", "Number lines") + .option("-b", "Number non-blank lines"); + +program.parse(); + +const paths = program.args; +const showN = program.opts().n; +const showB = program.opts().b; + +let lineNumber = 1; + +for (const path of paths) { + const content = await fs.readFile(path, "utf-8"); + + const endsWithNewline = content.endsWith("\n"); + let lines = content.split("\n"); + if (endsWithNewline) { + lines.pop(); + } + + lines = lines.map((line) => { + if (showB) { + + if (line === "") { + return line; + } + const numbered = String(lineNumber).padStart(6, " ") + "\t" + line; + lineNumber++; + return numbered; + } else if (showN) { + + const numbered = String(lineNumber).padStart(6, " ") + "\t" + line; + lineNumber++; + return numbered; + } else { + + return line; + } + }); + + let output = lines.join("\n"); + if (endsWithNewline) { + output += "\n"; + } + process.stdout.write(output); +} diff --git a/implement-shell-tools/cat/package-lock.json b/implement-shell-tools/cat/package-lock.json new file mode 100644 index 000000000..5cc805d7d --- /dev/null +++ b/implement-shell-tools/cat/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "cat", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^15.0.0" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + } + } +} diff --git a/implement-shell-tools/cat/package.json b/implement-shell-tools/cat/package.json new file mode 100644 index 000000000..cddd041aa --- /dev/null +++ b/implement-shell-tools/cat/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "commander": "^15.0.0" + } +} diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..5c3a3156c --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,46 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("check-for-ls") + .description("Implement my own version of ls") + .argument("[paths...]", "The file paths to process") + .option("-1, --one", "This lists one file per line") + .option("-a", "This shows all files"); + +program.parse(); + +const showOneLine = program.opts().one; +const showAllFiles = program.opts().a; +const paths = program.args; + +let filePath = paths; +if (filePath.length === 0) { + filePath = ["."]; +} + +for (const target of filePath) { + const info = await fs.stat(target); + + let showFiles; + if (info.isDirectory()) { + showFiles = await fs.readdir(target); + + if (showAllFiles) { + showFiles = [".", "..", ...showFiles]; + } else { + showFiles = showFiles.filter((name) => !name.startsWith(".")); + } + showFiles.sort(); + } else { + showFiles = [target]; + } + + if (showOneLine) { + for (const file of showFiles) { + console.log(file); + } + } else { + console.log(showFiles.join(" ")); + } +} diff --git a/implement-shell-tools/ls/package-lock.json b/implement-shell-tools/ls/package-lock.json new file mode 100644 index 000000000..09674c718 --- /dev/null +++ b/implement-shell-tools/ls/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "ls", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^15.0.0" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + } + } +} diff --git a/implement-shell-tools/ls/package.json b/implement-shell-tools/ls/package.json new file mode 100644 index 000000000..cddd041aa --- /dev/null +++ b/implement-shell-tools/ls/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "commander": "^15.0.0" + } +} diff --git a/implement-shell-tools/private-test/package-lock.json b/implement-shell-tools/private-test/package-lock.json new file mode 100644 index 000000000..0bdcfb87e --- /dev/null +++ b/implement-shell-tools/private-test/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "implement-shell-tools", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^15.0.0" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + } + } +} diff --git a/implement-shell-tools/private-test/package.json b/implement-shell-tools/private-test/package.json new file mode 100644 index 000000000..cddd041aa --- /dev/null +++ b/implement-shell-tools/private-test/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "commander": "^15.0.0" + } +} diff --git a/implement-shell-tools/private-test/test.js b/implement-shell-tools/private-test/test.js new file mode 100644 index 000000000..45947c6fa --- /dev/null +++ b/implement-shell-tools/private-test/test.js @@ -0,0 +1,27 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; +import process from "node:process"; + +program + .name("count-containing-words") + .description("Counts words in a file that contain a particular character") + .option("-c, --char ", "The character to search for", "e") + .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); +} +const path = argv[0]; +const char = program.opts().char; + +const content = await fs.readFile(path, "utf-8"); +const countOfWordsContainingChar = content + .split(" ") + .filter((word) => word.includes(char)).length; +console.log(countOfWordsContainingChar); diff --git a/implement-shell-tools/private-test/text.txt b/implement-shell-tools/private-test/text.txt new file mode 100644 index 000000000..d1b448c08 --- /dev/null +++ b/implement-shell-tools/private-test/text.txt @@ -0,0 +1,2 @@ +hello baby gorl +i like you \ No newline at end of file diff --git a/implement-shell-tools/wc/package-lock.json b/implement-shell-tools/wc/package-lock.json new file mode 100644 index 000000000..116f3635e --- /dev/null +++ b/implement-shell-tools/wc/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "wc", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "commander": "^15.0.0" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + } + } +} diff --git a/implement-shell-tools/wc/package.json b/implement-shell-tools/wc/package.json new file mode 100644 index 000000000..cddd041aa --- /dev/null +++ b/implement-shell-tools/wc/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "commander": "^15.0.0" + } +} diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 000000000..4a51e4f69 --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,76 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; + +program + .name("check-for-wc") + .description("Implement my own version of wc") + .argument("", "The file paths to process") + .option("-l", "Counts the total number of lines") + .option("-c", "Counts the total number of characters") + .option("-w", "Counts the total number of words"); + +program.parse(); + +const paths = program.args; +const showLines = program.opts().l; +const showWords = program.opts().w; +const showChar = program.opts().c; + +const noFlagsGiven = !showLines && !showWords && !showChar; + +const columns = []; + +for (const path of paths) { + const content = await fs.readFile(path, "utf-8"); + + const lineCount = content.split("\n").length - 1; + + const wordCount = content + .split(/\s+/) + .filter((word) => word.length > 0).length; + + const charCount = Buffer.byteLength(content, "utf-8"); + + columns.push({ + path: path, + lines: lineCount, + words: wordCount, + char: charCount, + }); +} +if (columns.length > 1) { + let totalLines = 0; + let totalWords = 0; + let totalChar = 0; + + for (const result of columns) { + totalLines += result.lines; + totalWords += result.words; + totalChar += result.char; + } + + columns.push({ + path: "total", + lines: totalLines, + words: totalWords, + char: totalChar, + }); +} + +for (const result of columns) { + let line = ""; + + if (noFlagsGiven || showLines) { + line += String(result.lines).padStart(6, " "); + } + if (noFlagsGiven || showWords) { + line += String(result.words).padStart(6, " "); + } + if (noFlagsGiven || showChar) { + line += String(result.char).padStart(6, " "); + } + + line += " " + result.path; + + console.log(line); +}