From 25a320afd9bf19c4a64ba46d382bf39d530c5743 Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Tue, 18 Mar 2025 16:14:30 +0000 Subject: [PATCH 1/6] write a simple script that reads file(no flag yet) --- implement-shell-tools/cat/cat.js | 19 +++++++++++++++++++ 1 file changed, 19 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 0000000..0aed46d --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,19 @@ +//A simple script that reads a file and prints its content.(no flags yet) +const fs = require("fs"); + +const args = process.argv; +const files = args.slice(2); // No flags yet, just file names + +console.log("args",args); +console.log("files",files); + +function readCatFile(filePath) { + try { + const content = fs.readFileSync(filePath, "utf8"); + console.log(content); + } catch (err){ + console.error(`error reading ${filePath}: ${err.message}`) + } +} + +files.forEach(file => readCatFile(file)) From 29aa174044aac93b62b70596f35f35759b80179a Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Mon, 24 Mar 2025 17:12:53 +0000 Subject: [PATCH 2/6] Installed Commander.js for command-line parsing --- implement-shell-tools/cat/package-lock.json | 24 +++++++++++++++++++++ implement-shell-tools/cat/package.json | 15 +++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 implement-shell-tools/cat/package-lock.json create mode 100644 implement-shell-tools/cat/package.json diff --git a/implement-shell-tools/cat/package-lock.json b/implement-shell-tools/cat/package-lock.json new file mode 100644 index 0000000..9f440e6 --- /dev/null +++ b/implement-shell-tools/cat/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "cat", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cat", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/implement-shell-tools/cat/package.json b/implement-shell-tools/cat/package.json new file mode 100644 index 0000000..07051e0 --- /dev/null +++ b/implement-shell-tools/cat/package.json @@ -0,0 +1,15 @@ +{ + "name": "cat", + "version": "1.0.0", + "description": "re-implementing shell tools like cat using Node.js", + "main": "cat.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Samira", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } +} From 6eff5a5ec38347cd010a3a1f6db5f342bcaa3633 Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Tue, 25 Mar 2025 13:11:41 +0000 Subject: [PATCH 3/6] Implement 'cat' command with line numbering --- implement-shell-tools/cat/cat.js | 44 ++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js index 0aed46d..f81f36c 100644 --- a/implement-shell-tools/cat/cat.js +++ b/implement-shell-tools/cat/cat.js @@ -1,19 +1,35 @@ -//A simple script that reads a file and prints its content.(no flags yet) -const fs = require("fs"); +import fs from "fs/promises"; // Import fs.promises for async file reading +import { Command } from "commander"; -const args = process.argv; -const files = args.slice(2); // No flags yet, just file names +const program = new Command(); -console.log("args",args); -console.log("files",files); +program + .command("readfiles ") // Accepts multiple file arguments + .description("read text files") + .option("-n, --number", "show line number") + .option("-b, --non-blank", "number 'only' the non-blank lines") + .action(async (files, options) => { + let lineCounter = 1; // Continuous line number for `-n` + let nonBlankCounter = 1; // Non-blank line number for `-b` -function readCatFile(filePath) { - try { - const content = fs.readFileSync(filePath, "utf8"); - console.log(content); - } catch (err){ - console.error(`error reading ${filePath}: ${err.message}`) + for (const file of files) { + try { + // Read file content using promises (async/await) + const data = await fs.readFile(file, "utf8"); + let lines = data.split("\n"); + + if (options.number) { + lines = lines.map((line) => `${lineCounter++} ${line}`); + } else if (options.nonBlank) { + lines = lines.map((line) => + line.trim() === "" ? line : `${nonBlankCounter++} ${line}` + ); + } + console.log(lines.join("\n")); + } catch (err) { + console.error(err); + } } -} + }); -files.forEach(file => readCatFile(file)) +program.parse(); From 95f303783053824cd956433c132e412888d8ad42 Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Sun, 30 Mar 2025 13:26:02 +0100 Subject: [PATCH 4/6] implement `ls` command with `-a` and `-1` options --- implement-shell-tools/ls/ls.js | 90 ++++++++++++++++++++++ implement-shell-tools/ls/package-lock.json | 24 ++++++ implement-shell-tools/ls/package.json | 15 ++++ 3 files changed, 129 insertions(+) create mode 100644 implement-shell-tools/ls/ls.js create mode 100644 implement-shell-tools/ls/package-lock.json create mode 100644 implement-shell-tools/ls/package.json diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 0000000..8ef57bb --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,90 @@ +//note to myself: +// path.resolve() converts a relative path into an absolute path. +//The last argument (dir) is resolved relative to the first argument. +//syntax: path.resolve(...paths); +//process.cwd() Returns the current working directory of the process (i.e., where the user ran the command from).It does NOT return the script’s directory. +//targetDir is created to store this resolved absolute path for reliable use in fs.readdirSync(). +import fs from "fs/promises"; +import { Command } from "commander"; +import path from "path"; + +const program = new Command(); + +program + .argument('[directory]', 'Directory to list', '.')//This specifies the directory argument. If no directory is provided, it defaults to the current directory (.) + .option("-l, --format", "List files in a single column") // Add -1 flag + .option("-a, --hidden", "Show hidden files (like ls -a)") // Add -a flag + .action(async (directory) => { + const resolvedPath = path.resolve(directory); + + const options = program.opts(); + + try { + const files = await fs.readdir(resolvedPath, { withFileTypes: true }); // files is an array of files inside the directory + //{ withFileTypes: true } returns file objects, allowing you to filter out hidden files if needed. + + + // If `-a` is NOT set, filter out hidden files (those starting with ".") + let filteredFiles = files.map(file => file.name); + + if(!options.hidden){ + filteredFiles = filteredFiles.filter(file => !file.startsWith(".")); + } + if(options.format){ + console.log(filteredFiles.join("\n"))// Print files in a single column (default `ls -1` behavior) + }else{ + console.log(filteredFiles.join(" "))// Print files space-separated (default `ls` behavior) + } + } catch (error) { + console.error(`Error reading directory: ${error.message}`); + process.exit(1); + } + }) + + program.parse(process.argv) + + + + + + + + +// async function readFile(directory) { +// console.log("this is from inside the function") +// try{ +// const files = await fs.readdir(directory);// Array of files in the directory +// console.log("files -->", files) +// console.log("files.join-->", files.join(' '))// String of files in the directory +// } catch (error) { +// console.error(`Error reading directory: ${error.message}`) +// process.exit(1); +// } +// } +// const directory = process.argv[2] || '.' +// console.log("directory-->", directory); +// readFile(directory) + + + + + +// const program = new Command(); + +// program +// .name("ls") +// .description("writing a simple ls implementation using commander.js") + +// program +// .command("ls") +// .description("lists files in the directory") +// .argument("[directory]","Directory to list", ".") +// .action((dir) => { +// const targetDir = path.resolve(process.cwd(), dir); + +// console.log("targetDir-->", targetDir) +// const files = fs.readdirSync(targetDir); +// console.log("files -->", files?.join(" ")); +// }) + +// program.parse(); \ No newline at end of file diff --git a/implement-shell-tools/ls/package-lock.json b/implement-shell-tools/ls/package-lock.json new file mode 100644 index 0000000..c83fb5e --- /dev/null +++ b/implement-shell-tools/ls/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "ls", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ls", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/implement-shell-tools/ls/package.json b/implement-shell-tools/ls/package.json new file mode 100644 index 0000000..8e7f62a --- /dev/null +++ b/implement-shell-tools/ls/package.json @@ -0,0 +1,15 @@ +{ + "name": "ls", + "version": "1.0.0", + "description": "\"writing ls implementation\"", + "main": "ls.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Samira", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } +} From 6221ea4e9e4fac0e664d5b351a3929e704ad0189 Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Sun, 30 Mar 2025 13:28:00 +0100 Subject: [PATCH 5/6] clean code --- implement-shell-tools/ls/ls.js | 48 +--------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js index 8ef57bb..108645c 100644 --- a/implement-shell-tools/ls/ls.js +++ b/implement-shell-tools/ls/ls.js @@ -41,50 +41,4 @@ program } }) - program.parse(process.argv) - - - - - - - - -// async function readFile(directory) { -// console.log("this is from inside the function") -// try{ -// const files = await fs.readdir(directory);// Array of files in the directory -// console.log("files -->", files) -// console.log("files.join-->", files.join(' '))// String of files in the directory -// } catch (error) { -// console.error(`Error reading directory: ${error.message}`) -// process.exit(1); -// } -// } -// const directory = process.argv[2] || '.' -// console.log("directory-->", directory); -// readFile(directory) - - - - - -// const program = new Command(); - -// program -// .name("ls") -// .description("writing a simple ls implementation using commander.js") - -// program -// .command("ls") -// .description("lists files in the directory") -// .argument("[directory]","Directory to list", ".") -// .action((dir) => { -// const targetDir = path.resolve(process.cwd(), dir); - -// console.log("targetDir-->", targetDir) -// const files = fs.readdirSync(targetDir); -// console.log("files -->", files?.join(" ")); -// }) - -// program.parse(); \ No newline at end of file + program.parse(process.argv); \ No newline at end of file From bff17f7108f914116c71e3482745a7da7b92d416 Mon Sep 17 00:00:00 2001 From: samirahekmati Date: Sun, 30 Mar 2025 16:25:53 +0100 Subject: [PATCH 6/6] Implement `wc` for counting lines, words & bytes --- implement-shell-tools/wc/package-lock.json | 24 ++++++++++ implement-shell-tools/wc/package.json | 15 +++++++ implement-shell-tools/wc/wc.js | 52 ++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 implement-shell-tools/wc/package-lock.json create mode 100644 implement-shell-tools/wc/package.json create mode 100644 implement-shell-tools/wc/wc.js diff --git a/implement-shell-tools/wc/package-lock.json b/implement-shell-tools/wc/package-lock.json new file mode 100644 index 0000000..943b9fd --- /dev/null +++ b/implement-shell-tools/wc/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "wc", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wc", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/implement-shell-tools/wc/package.json b/implement-shell-tools/wc/package.json new file mode 100644 index 0000000..477aab9 --- /dev/null +++ b/implement-shell-tools/wc/package.json @@ -0,0 +1,15 @@ +{ + "name": "wc", + "version": "1.0.0", + "description": "build a custom version of the wc with nodeJs", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Samira", + "license": "ISC", + "dependencies": { + "commander": "^13.1.0" + } +} diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 0000000..c6fdeb4 --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,52 @@ +import fs from "fs/promises"; +import { Command } from "commander"; + +const program = new Command(); //Creates a new instance of the Command class, which will handle our command-line arguments and commands. + +// Write a function to count lines, words and bytes + +async function count(file, options) { + const data = await fs.readFile(file, "utf8"); + + const linesCount = data.split("\n").length; + + const wordsCount = data.split(/\s+/).filter(Boolean).length; + + const bytes = Buffer.byteLength(data, "utf8"); + + let output = `${file}:`; //creates a string variable that will be used to store and build the final string that will be printed to the console. + + if (options.lines) { + output += ` ${linesCount}`; + console.log(output); + } + if (options.words) { + output += ` ${wordsCount}`; + console.log(output); + } + + if (options.bytes) { + output += `${bytes}`; + console.log(output); + } + + // If no options were provided, show all counts + if (!options.lines && !options.words && !options.bytes) { + output += ` ${linesCount} ${wordsCount} ${bytes}`; + console.log(output); + } +} + +program + .command("wc ") //The syntax means it accepts one or more file names as arguments. + .description("Count lines, words, and bytes in text files") + .option("-l, --lines", "Count lines") + .option("-w, --words", "Count words") + .option("-c, --bytes", "Count bytes") + .action(async (files, options) => { + for (const file of files) { + await count(file, options); + } + }); + +program.parse();