-
-
Notifications
You must be signed in to change notification settings - Fork 42
WestMidlands | SDC-NOV-2025 | SARA TAHIR | Sprint 3 | Implement Shell Tools #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
de68e2e
fdbd244
007ee54
56e2740
1bcd513
e44078e
99aae23
e8c2041
6152444
9463024
3de9968
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Node modules | ||
| node_modules/ | ||
|
|
||
| # Package lock file (optional) | ||
| package-lock.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| //On macOS (BSD cat), the -n option resets numbering for each file, so multiple files start again at 1, | ||
| //when you run cat -n sample-files/*.txt | ||
| //This Node.js script mimics GNU cat, where -n continues numbering across all files instead of restarting. | ||
| import { program } from "commander"; | ||
| import {promises as fs} from "node:fs" | ||
|
|
||
| //configuring the program here to run flags. | ||
|
|
||
| program | ||
| .name("cat") | ||
| .description("Concatenate and print files") | ||
| .option("-n, --number", "Number all output lines") | ||
| .option("-b, --number-nonblank", "Number non-blank output lines") | ||
| .argument("<files...>", "File paths to display") | ||
| .parse(process.argv);//Parse command line arguments (reads process.argv and interprets it) | ||
|
|
||
|
|
||
| //Constants | ||
| const paddingWidth = 6; // width for line number padding | ||
|
|
||
| // Helper function to format a line with numbering | ||
| function formatLine(line, lineNumber, numberAll, numberNonBlank) { | ||
| if (numberAll) { | ||
| return `${lineNumber.toString().padStart(paddingWidth)} ${line}`; | ||
| } | ||
| if (numberNonBlank) { | ||
| if (line.trim() === "") { | ||
| return line; // blank line, no number | ||
| } | ||
| return `${lineNumber.toString().padStart(paddingWidth)} ${line}`; | ||
| } | ||
| return line; // no numbering | ||
| } | ||
| const options = program.opts(); | ||
| const files = program.args; // Array of file paths passed as arguments | ||
|
|
||
| let lineNumber = 1; // shared across all files | ||
|
|
||
| for (const file of files) { | ||
| const content = await fs.readFile(file, "utf-8"); | ||
| let lines = content.split("\n"); | ||
| // Remove trailing empty line if file ends with newline | ||
| if (lines.length > 0 && lines[lines.length - 1] === "") { | ||
| lines.pop(); | ||
| } | ||
|
|
||
| for (const line of lines) { | ||
| const formatted = formatLine(line, lineNumber, options.number, options.numberNonblank); | ||
| console.log(formatted); | ||
|
|
||
| // Increment line number if numbering applies | ||
| if (options.number || (options.numberNonblank && line.trim() !== "")) { | ||
| lineNumber++; | ||
| } | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nice, cleanly written implementation |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { program } from "commander"; | ||
| import { promises as fs } from "node:fs"; | ||
|
|
||
| //configuring | ||
| program | ||
| .name("ls") | ||
| .description("list directory contents") | ||
| .option("-1, --one", "Outputs are printed one entry per line") | ||
| .option("-a, --all","Show all files including hidden files that start with a .") | ||
| .argument("[directory]", "Directory to list", "."); // "." means current directory | ||
|
|
||
| //interpret the program | ||
| program.parse(); | ||
| const options = program.opts(); | ||
| const directory = program.args[0] || "."; //get dir arg- 1st arg in program.args array || if no arg default to current dir | ||
|
|
||
|
|
||
| let files = await fs.readdir(directory); //read the dir to get array of filenames | ||
|
|
||
| //Handle -a (include hidden files) | ||
| // Node's fs.readdir() does not include the special directory entries "." (current dir) | ||
| // and ".." (parent dir). The real `ls -a` command shows them, so we add them manually here | ||
| // to match the behavior of `ls -a`. | ||
| if (options.all) { | ||
|
|
||
| files.unshift(".."); | ||
| files.unshift("."); | ||
| } else { | ||
| files = files.filter(name => !name.startsWith(".")); | ||
| } | ||
|
|
||
| if (options.one) { // Print each file on its own line | ||
| for (const file of files) { | ||
| console.log(file); | ||
| } | ||
| } | ||
| else { | ||
| console.log(files.join(" "));// Default: join with spaces (like ls without -1) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "type": "module", | ||
| "dependencies": { | ||
| "commander": "^14.0.2" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import { program } from "commander"; | ||
| import { promises as fs } from "node:fs"; | ||
|
|
||
| //configuring | ||
| program | ||
| .name("wc") | ||
| .description("Print newline, word, and byte counts for each file") | ||
| .option("-l, --lines", "Print the newline counts") | ||
| .option("-w, --words", "Print the word counts") | ||
| .option("-c, --bytes", "Print the byte counts") | ||
| .argument("<files...>", "File paths to process") | ||
| //Interpret the program | ||
| program.parse() | ||
| const options = program.opts() | ||
| const files = program.args //Interpreting parsed data | ||
|
|
||
|
|
||
| //helper function to calculate all counts | ||
| async function getCounts(file){ | ||
| const content = await fs.readFile(file, "utf-8"); | ||
| const lineCount = content.split("\n").length -1;//split("\n") returns one more element than the actual number so length-1, | ||
| const wordCount = content.trim().split(/\s+/).length; | ||
| const byteCount = Buffer.byteLength(content, "utf-8"); //Calculate how many bytes the string uses in UTF-8 (important because some characters use more than 1 byte) | ||
| return { lineCount, wordCount, byteCount }; | ||
| } | ||
| //initiating totals | ||
| let totalLines = 0; | ||
| let totalWords = 0; | ||
| let totalBytes = 0; | ||
|
|
||
| for (const file of files) { | ||
| const { lineCount, wordCount, byteCount } = await getCounts(file); | ||
|
|
||
| totalLines += lineCount; | ||
| totalWords += wordCount; | ||
| totalBytes += byteCount; | ||
|
|
||
| let output = ""; | ||
| if (options.lines) output += `${lineCount} `; | ||
|
||
| if (options.words) output += `${wordCount} `; | ||
| if (options.bytes) output += `${byteCount} `; | ||
| if (!options.lines && !options.words && !options.bytes) { | ||
| output += `${lineCount} ${wordCount} ${byteCount} `; | ||
| } | ||
| output += file; | ||
| console.log(output); | ||
| } | ||
|
|
||
|
|
||
| //Print totals if multiple files | ||
| if (files.length > 1) { | ||
| let totalOutput = ""; | ||
| if (options.lines) totalOutput += `${totalLines} `; | ||
| if (options.words) totalOutput += `${totalWords} `; | ||
| if (options.bytes) totalOutput += `${totalBytes} `; | ||
| if (!options.lines && !options.words && !options.bytes) { | ||
| totalOutput += `${totalLines} ${totalWords} ${totalBytes} `; | ||
| } | ||
| totalOutput += "total"; | ||
| console.log(totalOutput); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea to pull this function out, but you could have taken advantage of this to reduce the duplication of the string formatting code, which appears twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thankyou for pointing that out, I have removed teh duplication.