diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 0000000..f81f36c --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,35 @@ +import fs from "fs/promises"; // Import fs.promises for async file reading +import { Command } from "commander"; + +const program = new Command(); + +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` + + 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); + } + } + }); + +program.parse(); 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" + } +} diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 0000000..108645c --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,44 @@ +//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); \ 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" + } +} 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();