diff --git a/implement-shell-tools/.gitignore b/implement-shell-tools/.gitignore new file mode 100644 index 000000000..1d17dae13 --- /dev/null +++ b/implement-shell-tools/.gitignore @@ -0,0 +1 @@ +.venv diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..4c9b552a7 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,48 @@ +import process from "node:process"; +import { promises as fs } from "node:fs"; + +// THis will give an array without the path to node and to the file. +const argv = process.argv.slice(2); + +//Get line numbers. +const showNumbers = argv.includes("-n"); +const showNonBlankNumbers = argv.includes("-b"); + +//filter the - from the array argv as it's a flag. +const filePaths = argv.filter((arg) => !arg.startsWith("-")); + +const flagsUsed = argv.filter((arg) => arg.startsWith("-")); +const supportedFlags = ["-n", "-b"]; +for (const flag of flagsUsed) { + if (!supportedFlags.includes(flag)) { + console.error(`Invalid option try 'node cat.js --help' for more info.`); + process.exit(1); + } +} + +let counterLines = 1; + +for (const path of filePaths) { + try { + const content = await fs.readFile(path, "utf-8"); + + //split the text at the new line character. + const splitLines = content.split("\n"); + + splitLines.forEach((line) => { + let prefix = ""; + + if (showNonBlankNumbers) { + if (line.trim() !== "") { + prefix = `${counterLines++} `; + } + } else if (showNumbers) { + prefix = `${counterLines++} `; + } + console.log(`${prefix}${line}`); + }); + } catch (error) { + console.error(`Could not read: ${path}`); + process.exit(1); + } +} diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..e1d3b4b75 --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,32 @@ +import fs from "node:fs"; +import process from "node:process"; + +// This will give an array without the path to node and to the file. +const argv = process.argv.slice(2); + +// filter the flag to find the target folder. +const filePaths = argv.filter((arg) => !arg.startsWith("-")); +const showHiddenFiles = argv.includes("-a"); + +if (filePaths.length > 1) { + console.error("Error: This version only supports one directory path a time."); + process.exit(1); +} +// if no folder provide we use the current one +const target = filePaths[0] || "."; +// read the file. +const files = fs.readdirSync(target); + +// save the result into the variable. +let filteredFIles = files; +if (!showHiddenFiles) { + filteredFIles = files.filter((file) => !file.startsWith(".")); +} else { + // we use spread operator to merge the paths. + filteredFIles = [".", "..", ...files]; +} + +// Print using -1 . +filteredFIles.forEach((file) => { + console.log(file); +}); diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 000000000..44e1a1429 --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,46 @@ +import process, { exit } from "node:process"; +import fs from "node:fs"; + +const argv = process.argv.slice(2); + +let showWords = argv.includes("-w"); +let showLines = argv.includes("-l"); +let showBytes = argv.includes("-c"); +let showCharacters = argv.includes("-m"); + +// filter flags, and getting the string of filename +const filePaths = argv.filter((arg) => !arg.startsWith("-")); +// if no flags enable all. +if (!showLines && !showCharacters && !showWords && !showBytes) { + showLines = true; + showCharacters = true; + showWords = true; + showBytes = true; +} +// fix bug .length ===0 will be true if nothing provided, instead !filePath will return empty array which result true. +if (filePaths.length === 0) { + console.error("PLease provide a file path"); + process.exit(1); +} +// loop trough the array to get each file path. +filePaths.forEach((filePath) => { + const content = fs.readFileSync(filePath, "utf-8"); + + const lines = content.split("\n").length - 1; + const words = content + .trim() + .split(/\s+/) + .filter((word) => word != "").length; + // here I used Buffer.byteLength even if characters and bytes can be the same number .length, however sometimes an emoji or special characters can be heavier 2b or 4b + const bytes = Buffer.byteLength(content); + const characters = content.length; + + let output = ""; + + if (showLines) output += `${lines} `; + if (showWords) output += `${words} `; + if (showBytes) output += `${bytes} `; + if (showCharacters) output += `${characters} `; + + console.log(`${output} ${filePath}`); +});