-
-
Notifications
You must be signed in to change notification settings - Fork 13
London SDC | Samira Hekmati | Module-Tools | Implement-Shell-Tools in JS | Week 3 #53
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 all commits
25a320a
29aa174
6eff5a5
95f3037
6221ea4
bff17f7
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,35 @@ | ||
import fs from "fs/promises"; // Import fs.promises for async file reading | ||
import { Command } from "commander"; | ||
|
||
const program = new Command(); | ||
|
||
program | ||
.command("readfiles <files...>") // 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) { | ||
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. There seems to be something wrong with the output. When I run this code, the lines on multiple files are counted the with the same counter: $ node cat.js -b sample-files/*.txt
1 Once upon a time...
2 There was a house made of gingerbread.
3 It looked delicious.
4 I was tempted to take a bite of it.
5 But this seemed like a bad idea...
6 There's more to come, though...
However, when we run cat, we can see that every line number is counted individually: ➜ cat -b sample-files/*.txt
1 Once upon a time... # From sample-files/1.txt
1 There was a house made of gingerbread. # From sample-files/2.txt
1 It looked delicious. # From sample-files/3.txt
2 I was tempted to take a bite of it.
3 But this seemed like a bad idea...
4 There's more to come, though... How can we modify the code so that the lines are counted correctly? |
||
lines = lines.map((line) => | ||
line.trim() === "" ? line : `${nonBlankCounter++} ${line}` | ||
); | ||
} | ||
console.log(lines.join("\n")); | ||
} catch (err) { | ||
console.error(err); | ||
} | ||
} | ||
}); | ||
|
||
program.parse(); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
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. Do we need this script? For a stretch goal, is there a script you could write to run cat with |
||
}, | ||
"author": "Samira", | ||
"license": "ISC", | ||
"dependencies": { | ||
"commander": "^13.1.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(). | ||
Comment on lines
+1
to
+6
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. These notes are great! In a real code submission I'd ask you to remove them, but as this is a learning exercise, well done getting to the bottom of this! The same applies with the rest of the comments. In general, it's safe to assume that the person reading the code has easy access to the documentation, so you don't need to explain, for instance, what the arguments to |
||
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 | ||
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. When ➜ ls -1 -a sample-files
.
..
.hidden.txt
1.txt
2.txt
3.txt
dir How could we modify this solution so that we do print them? |
||
.action(async (directory) => { | ||
const resolvedPath = path.resolve(directory); | ||
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. What happens when we receive more than one argument? (e.g. |
||
|
||
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) | ||
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. In ls, files are printed with a certain amount of padding, so that if we have many files, they appear to be in a table:
This is not part of the assignment brief but, as a streacth goal, would you like to consider how to pad the outputs like that? |
||
} | ||
} catch (error) { | ||
console.error(`Error reading directory: ${error.message}`); | ||
process.exit(1); | ||
} | ||
}) | ||
|
||
program.parse(process.argv); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <files...>") //The <files...> 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") | ||
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. In ➜ wc -l sample-files/*
1 sample-files/1.txt
1 sample-files/2.txt
5 sample-files/3.txt
7 total How could we implement that functionality into this program? 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. When counting lines, your program counts one more than ➜ wc -l sample-files/3.txt
5 sample-files/3.txt
➜ node wc.js wc -l sample-files/3.txt
sample-files/3.txt: 6 Why does this happen? How could we fix it so that we print the right number of lines? |
||
.option("-w, --words", "Count words") | ||
.option("-c, --bytes", "Count bytes") | ||
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. In your program, numbers are left-aligned: ➜ node wc.js wc -c sample-files/*
sample-files/1.txt:20
sample-files/2.txt:39
sample-files/3.txt:125 However, in ➜ wc -c sample-files/*
20 sample-files/1.txt
39 sample-files/2.txt
125 sample-files/3.txt
184 total Note how it's easy to tell that 35 has one digit less than 125. |
||
.action(async (files, options) => { | ||
for (const file of files) { | ||
await count(file, options); | ||
} | ||
}); | ||
|
||
program.parse(); |
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.
Comments can get out of date. Is there a way you can codify this intention in the code? e.g. via variable name.