diff --git a/implement-shell-tools/cat/customCat.js b/implement-shell-tools/cat/customCat.js new file mode 100644 index 000000000..2ac037b44 --- /dev/null +++ b/implement-shell-tools/cat/customCat.js @@ -0,0 +1,51 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; +import process from "node:process"; + +program + .name("custom-cat") + .description("my-own-version-of-cat") + .option("-n, --line", "Adding a number before each roll") + .option( + "-b, --nonBlank", + "Only adding a number before roll that is non blank", + ) + .argument("", "The file path to process"); + +program.parse(); + +const options = program.opts(); + +const argumentArray = program.args; +if (argumentArray.length < 1) { + console.log( + `We need at least 1 path of file to process but we got ${argumentArray.length}`, + ); + process.exit(1); +} + +const pathsArray = argumentArray; + +let count = 1; + +for (let path of pathsArray) { + const context = await fs.readFile(path, "utf-8"); + const lines = context.trimEnd().split("\n"); + if (options.nonBlank) { + lines.forEach((line) => { + if (line.length != 0) { + console.log(` ${count} ${line}`); + count++; + } else { + console.log(line); + } + }); + } else if (options.line) { + lines.forEach((line) => { + console.log(` ${count} ${line}`); + count++; + }); + } else { + console.log(context.trimEnd()); + } +} diff --git a/implement-shell-tools/ls/customLs.js b/implement-shell-tools/ls/customLs.js new file mode 100644 index 000000000..76903f62e --- /dev/null +++ b/implement-shell-tools/ls/customLs.js @@ -0,0 +1,72 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; +import process from "node:process"; + +program + .name("Custom-ls") + .description("Custom-ls-that-works-like-ls") + .option("-1, --oneFile", "Showing one file per line") + .option("-a, --showHidden", "Showing hidden files") + .argument("[path]", "The file path to process"); + +program.parse(); + +const argumentsArray = program.args; + +const path = argumentsArray[0] || "./"; + +async function formatFileName(base, fileName) { + try { + const fullPath = `${base.endsWith("/") ? base : base + "/"}${fileName}`; + const stats = await fs.stat(fullPath); + if (stats.isDirectory()) { + return `\x1b[1;34m${fileName}\x1b[0m`; + } + return fileName; + } catch (error) { + return fileName; + } +} + +try { + const files = await fs.readdir(path); + + const sortedFiles = files.sort((a, b) => { + const cleanA = a.replace(/^\./, ""); + const cleanB = b.replace(/^\./, ""); + return cleanA.localeCompare(cleanB, undefined, { sensitivity: "base" }); + }); + + const options = program.opts(); + + let renderingFiles = []; + + if (options.showHidden) { + renderingFiles = [".", "..", ...sortedFiles]; + } else { + renderingFiles = sortedFiles.filter((file) => !/^\./.test(file)); + } + + if (options.oneFile) { + for (let file of renderingFiles) { + console.log(await formatFileName(path, file)); + } + } else { + const formatted = await Promise.all( + renderingFiles.map((fileName) => { + return formatFileName(path, fileName); + }), + ); + console.log(formatted.join(" ")); + } +} catch (error) { + const stats = await fs.stat(path).catch(() => null); + if (stats && stats.isFile()) { + console.log(path); + } else { + console.log( + `Can't access to this path: ${path} - No such file or directory`, + ); + process.exit(1); + } +} diff --git a/implement-shell-tools/package-lock.json b/implement-shell-tools/package-lock.json new file mode 100644 index 000000000..8cd4b3de8 --- /dev/null +++ b/implement-shell-tools/package-lock.json @@ -0,0 +1,21 @@ +{ + "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==", + "license": "MIT", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/implement-shell-tools/package.json b/implement-shell-tools/package.json new file mode 100644 index 000000000..402b2a4e9 --- /dev/null +++ b/implement-shell-tools/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "commander": "^14.0.3" + }, + "type": "module" +} diff --git a/implement-shell-tools/wc/customWc.js b/implement-shell-tools/wc/customWc.js new file mode 100644 index 000000000..037eb64d9 --- /dev/null +++ b/implement-shell-tools/wc/customWc.js @@ -0,0 +1,83 @@ +import { program } from "commander"; +import { promises as fs } from "node:fs"; +import process from "node:process"; + +program + .name("Custom-wc") + .description("Custom-wc-that-works-like-wc") + .option("-l, --lines", "Counting lines in the file") + .option("-w, --words", "Counting words in the file") + .option("-c, --characters", "Counting characters in the file") + .argument("", "Path of file to process"); + +program.parse(); + +const argumentArray = program.args; +if (argumentArray.length === 0) { + console.log(`We need at least one file path to process`); + process.exit(1); +} + +const pathArray = argumentArray; +const options = program.opts(); + +function padStartNumbers(...args) { + const space = [3, 4, 4]; + const numberStringArray = []; + for (let index = 0; index < args.length; index++) { + numberStringArray.push(String(args[index]).padStart(space[index], " ")); + } + return numberStringArray.join(""); +} + +const totalRowNumbers = []; + +let totalOfLines = 0; +let totalOfWords = 0; +let totalOfCharacters = 0; + +for (let path of pathArray) { + let numberOfLines = 0; + let numberOfWords = 0; + let numberOfCharacters = 0; + + const file = await fs.readFile(path, "utf-8"); + numberOfLines = file.split("\n").length - 1; + const words = file.match(/\S+/g); + numberOfWords = words ? words.length : 0; + numberOfCharacters = file.length; + + const rowNumbers = []; + + if (options.lines) rowNumbers.push(numberOfLines); + if (options.words) rowNumbers.push(numberOfWords); + if (options.characters) rowNumbers.push(numberOfCharacters); + + if (rowNumbers.length === 0) { + console.log( + `${padStartNumbers(numberOfLines, numberOfWords, numberOfCharacters)} ${path}`, + ); + } else { + if (pathArray.length === 1 && rowNumbers.length === 1) { + console.log(`${rowNumbers[0]} ${path}`); + } else { + console.log(`${padStartNumbers(...rowNumbers)} ${path}`); + } + } + totalOfLines += numberOfLines; + totalOfWords += numberOfWords; + totalOfCharacters += numberOfCharacters; +} + +if (pathArray.length > 1) { + if (options.lines) totalRowNumbers.push(totalOfLines); + if (options.words) totalRowNumbers.push(totalOfWords); + if (options.characters) totalRowNumbers.push(totalOfCharacters); + if (totalRowNumbers.length > 0) { + console.log(`${padStartNumbers(...totalRowNumbers)} total`); + } else { + console.log( + `${padStartNumbers(totalOfLines, totalOfWords, totalOfCharacters)} total`, + ); + } +}