From 78bdfd5f5130b16853693f87d5b920acfa710cda Mon Sep 17 00:00:00 2001 From: Alex Jamshidi Date: Tue, 28 Jul 2026 10:08:03 +0100 Subject: [PATCH 1/5] cat implemented --- implement-shell-tools/cat/cat.js | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 implement-shell-tools/cat/cat.js diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..e21ac51e0 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,46 @@ +const fs = require("fs"); + +const arguments = process.argv; +const filePaths = arguments.slice(2); + +const index = arguments[1].lastIndexOf("/"); +const currentWorkingDirectory = arguments[1].slice(0, index + 1); + +let flag = filePaths[0]; +if (flag.charAt(0) == "-") { + flag = filePaths.shift(); +} + +function splitFile(fileName) { + const filePath = currentWorkingDirectory + fileName; + const text = fs.readFileSync(filePath, "utf8").trimEnd(); + return text.split("\n"); +} + +function printFile(lines) { + lines.forEach((line) => { + console.log(line); + }); +} + +function executeFlag(lines) { + if (flag === "-n") { + return lines.map((line, index) => `${String(index + 1).padStart(6, " ")} ${line}`); + } else if (flag === "-b") { + let lineNumber = 1; + return lines.map((line) => { + if (line === "") { + return `${line}`; + } + return `${String(lineNumber++).padStart(6, " ")} ${line}`; + }); + } else { + return lines; + } +} + +filePaths.forEach((filePath) => { + let lines = splitFile(filePath); + lines = executeFlag(lines); + printFile(lines); +}); From 60b323e23467c6d21b1abe736ddcb10bd41f8c4e Mon Sep 17 00:00:00 2001 From: Alex Jamshidi Date: Tue, 28 Jul 2026 20:10:51 +0100 Subject: [PATCH 2/5] cat updated --- implement-shell-tools/cat/cat.js | 77 +++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index e21ac51e0..e9dcd9147 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -1,46 +1,73 @@ const fs = require("fs"); const arguments = process.argv; -const filePaths = arguments.slice(2); +const userArguments = arguments.slice(2); const index = arguments[1].lastIndexOf("/"); const currentWorkingDirectory = arguments[1].slice(0, index + 1); -let flag = filePaths[0]; -if (flag.charAt(0) == "-") { - flag = filePaths.shift(); +const flags = userArguments + .filter((argument) => argument.startsWith("-")) + .map((argument) => argument.slice(1)) + .join(""); + +const flagHandlers = { + b: bFlag, + n: nFlag, +}; + +const fileNames = userArguments.filter((argument) => !argument.startsWith("-")); +let allFilesContents = []; + +readFiles(); +executeFlags(); +printLines(); + +function readFiles() { + fileNames.forEach((fileName) => { + fileContent = readFile(fileName); + allFilesContents.push(fileContent.split("\n")); + }); } -function splitFile(fileName) { +function readFile(fileName) { const filePath = currentWorkingDirectory + fileName; - const text = fs.readFileSync(filePath, "utf8").trimEnd(); - return text.split("\n"); + return fs.readFileSync(filePath, "utf8").trimEnd(); } -function printFile(lines) { - lines.forEach((line) => { - console.log(line); - }); +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error(`cat: illegal option -- ${flag}\nusage: cat [-belnstuv] [file ...]`); + process.exit(1); + } + } } -function executeFlag(lines) { - if (flag === "-n") { - return lines.map((line, index) => `${String(index + 1).padStart(6, " ")} ${line}`); - } else if (flag === "-b") { +function bFlag() { + return allFilesContents.map((fileContent) => { let lineNumber = 1; - return lines.map((line) => { - if (line === "") { + return fileContent.map((line, index) => { + if (line == "") { return `${line}`; } return `${String(lineNumber++).padStart(6, " ")} ${line}`; }); - } else { - return lines; - } + }); +} + +function nFlag() { + return allFilesContents.map((fileContent) => { + return fileContent.map((line, index) => `${String(index + 1).padStart(6, " ")} ${line}`); + }); } -filePaths.forEach((filePath) => { - let lines = splitFile(filePath); - lines = executeFlag(lines); - printFile(lines); -}); +function printLines() { + allFilesContents.forEach((fileContent) => { + fileContent.forEach((line) => { + console.log(line); + }); + }); +} From 3a35a7a48f3d8a52b2c79ead79cfd7db0a1bc30d Mon Sep 17 00:00:00 2001 From: Alex Jamshidi Date: Wed, 29 Jul 2026 00:56:56 +0100 Subject: [PATCH 3/5] wc implemented --- implement-shell-tools/wc/wc.js | 141 +++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 implement-shell-tools/wc/wc.js diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 000000000..9635e8e3f --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,141 @@ +const fs = require("fs"); +const { allowedNodeEnvironmentFlags } = require("process"); + +const arguments = process.argv; +const userArguments = arguments.slice(2); + +const index = arguments[1].lastIndexOf("/"); +const currentWorkingDirectory = arguments[1].slice(0, index + 1); + +const flags = userArguments + .filter((argument) => argument.startsWith("-")) + .map((argument) => argument.slice(1)) + .join(""); + +const flagHandlers = { + l: lFlag, + w: wFlag, + c: cFlag, +}; + +const metrics = ["lineCount", "wordCount", "byteSize"]; +const fileNames = userArguments.filter((argument) => !argument.startsWith("-")); +const allFilesData = []; + +extractFilesData(); +addTotals(); +const outputData = structuredClone(allFilesData); + +let deleted = false; +executeFlags(); +printOutput(); + +function extractFilesData() { + fileNames.forEach((fileName) => { + const fileData = {}; + fileData.name = fileName; + fileData.text = readFile(fileName); + fileData.lineCount = calculateLineCount(fileData.text); + fileData.wordCount = calculateWordCount(fileData.text); + fileData.byteSize = readByteSize(fileName); + + allFilesData.push(fileData); + }); +} + +function addTotals() { + if (allFilesData.length == 1) { + return; + } + const totalsData = { name: "total" }; + metrics.forEach((metric) => { + let sum = 0; + allFilesData.forEach((file) => { + sum += file[metric]; + }); + totalsData[metric] = sum; + }); + allFilesData.push(totalsData); +} + +function readFile(fileName) { + const filePath = currentWorkingDirectory + fileName; + return fs.readFileSync(filePath, "utf8").trimEnd(); +} + +function calculateLineCount(text) { + return text.split("\n").length; +} + +function calculateWordCount(text) { + return text.split(/\s+/).length; +} + +function readByteSize(fileName) { + return fs.statSync(currentWorkingDirectory + fileName).size; +} + +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error(`wc: illegal option -- ${flag}\nusage: wc [-Lclmw] [file ...]`); + process.exit(1); + } + } +} + +function lFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.lineCount = getIfTrue(sourceFile, "lineCount"); + }); +} + +function wFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.wordCount = getIfTrue(sourceFile, "wordCount"); + }); +} + +function cFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.byteSize = getIfTrue(sourceFile, "byteSize"); + }); +} + +function deleteOutputs() { + if (!deleted) { + outputData.forEach((file) => { + metrics.forEach((metric) => { + delete file[metric]; + }); + }); + } + deleted = true; +} + +function printOutput() { + outputs = []; + outputData.forEach((file) => { + let outputString = ""; + metrics.forEach((metric) => { + outputString += getIfTrue(file, metric); + }); + outputString += ` ${file.name}`; + console.log(outputString); + }); +} + +function getIfTrue(file, metric) { + if (file[metric]) { + return String(file[metric]).padStart(8, " "); + } + return ""; +} From 085224e36b79d11885b390b6ab7cbb165b1ff481 Mon Sep 17 00:00:00 2001 From: Alex Jamshidi Date: Thu, 30 Jul 2026 17:17:26 +0100 Subject: [PATCH 4/5] ls complete --- implement-shell-tools/ls/ls.js | 124 +++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 implement-shell-tools/ls/ls.js diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..6f1113671 --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,124 @@ +const fs = require("fs"); +const { allowedNodeEnvironmentFlags } = require("process"); + +const args = process.argv; +const userArgs = args.slice(2); + +const index = args[1].lastIndexOf("/"); +const currentWorkingDirectory = args[1].slice(0, index); + +const flags = userArgs + .filter((arg) => arg.startsWith("-")) + .map((arg) => arg.slice(1)) + .join(""); + +const flagHandlers = { + 1: Flag1, + a: Flaga, +}; + +let printInList = false; +let showAll = false; + +const fsItems = userArgs.filter((arg) => !arg.startsWith("-")); +let dirArgs; +let fileArgs; +let outputString = ""; + +const rawData = fs.readdirSync(currentWorkingDirectory); + +checkArgsLength(); +executeFlags(); +filterFilesAndDirs(); +populateOutput(); +print(); + +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error( + `ls: invalid option -- ${flag}\nusage: ls [-@ABCFGHILOPRSTUWXabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]`, + ); + process.exit(1); + } + } +} + +function Flag1() { + if (printInList) { + outputString = outputString.replaceAll("\t", "\n"); + outputString = outputString.replaceAll("\n\n", "\n"); + } else { + printInList = true; + } +} + +function Flaga() { + showAll = true; +} + +function checkArgsLength() { + if (fsItems.length == 0) { + fsItems.push("."); + } +} + +function filterFilesAndDirs() { + dirArgs = fsItems.filter((p) => fs.statSync(currentWorkingDirectory + "/" + p).isDirectory()); + fileArgs = fsItems.filter((p) => !fs.statSync(currentWorkingDirectory + "/" + p).isDirectory()); + if (showAll == false) { + removeDotFiles(fileArgs); + } +} + +function populateOutput() { + if (fsItems.length == 1) { + fileArgs.forEach((file) => { + outputString += file + " "; + }); + dirArgs.forEach((dir) => { + outputString += dirOutput(dir); + }); + } else { + fileArgs.forEach((file) => { + outputString += file + "\t"; + }); + dirArgs.forEach((dir) => { + outputString += "\n\n" + dir + ":\n" + dirOutput(dir); + }); + } +} + +function dirOutput(dir) { + let contents = fs.readdirSync(currentWorkingDirectory + "/" + dir); + let outputStr = ""; + if (showAll) { + outputStr += ".\t..\t"; + } else { + for (let i = contents.length - 1; i >= 0; i--) { + contents = removeDotFiles(contents); + } + } + contents.forEach((item) => { + outputStr += item + "\t"; + }); + return outputStr; +} + +function removeDotFiles(fileList) { + for (let i = fileList.length - 1; i >= 0; i--) { + if (fileList[i][0] == ".") { + fileList.splice(i, 1); + } + } + return fileList; +} + +function print() { + if (printInList == true) { + Flag1(); + } + console.log(outputString.trimEnd()); +} From b28f41dbb8b34c4594b13b4dd36aee9b001e6ba8 Mon Sep 17 00:00:00 2001 From: Alex Jamshidi Date: Fri, 31 Jul 2026 03:27:27 +0100 Subject: [PATCH 5/5] remove unused code --- implement-shell-tools/ls/ls.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js index 6f1113671..8646dfef6 100644 --- a/implement-shell-tools/ls/ls.js +++ b/implement-shell-tools/ls/ls.js @@ -25,8 +25,6 @@ let dirArgs; let fileArgs; let outputString = ""; -const rawData = fs.readdirSync(currentWorkingDirectory); - checkArgsLength(); executeFlags(); filterFilesAndDirs();