-
-
Notifications
You must be signed in to change notification settings - Fork 88
Manchester | 25-SDC-Nov | Rahwa Haile | Sprint 3 | Implement Shell Tools #244
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 3 commits
d6c3aaa
2ab9eea
da8b88e
b476ec7
bc84111
50ae619
4787a6b
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,79 @@ | ||
| #!/usr/bin/env node | ||
| const { program } = require("commander"); | ||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
|
|
||
|
|
||
|
|
||
| function expandWildcard(pattern) { | ||
| const dir = path.dirname(pattern); | ||
| const base = path.basename(pattern); | ||
|
|
||
| if (!base.includes("*")) return [pattern]; | ||
|
|
||
| let files; | ||
| try { | ||
| files = fs.readdirSync(dir); | ||
| } catch (e) { | ||
| console.error(`cat: ${pattern}: No such directory`); | ||
| return []; | ||
| } | ||
|
|
||
| const regex = new RegExp("^" + base.replace(/\*/g, ".*") + "$"); | ||
|
|
||
| return files | ||
| .filter((f) => regex.test(f)) | ||
| .map((f) => path.join(dir, f)); | ||
| } | ||
|
|
||
| function printFile(filename, options) { | ||
| let text; | ||
| try { | ||
| text = fs.readFileSync(filename, "utf-8"); | ||
| } catch { | ||
| console.error(`cat: ${filename}: No such file`); | ||
| return; | ||
| } | ||
|
|
||
| const lines = text.split("\n"); | ||
| if (lines[lines.length - 1] === "") { | ||
| lines.pop(); | ||
| } | ||
| let counter = 1; | ||
|
|
||
| lines.forEach((line) => { | ||
| if (options.numberAll) { | ||
| const paddingSize = 6; | ||
| console.log(`${String(counter).padStart(paddingSize)} ${line}`); | ||
| counter++; | ||
| } else if (options.numberNonempty) { | ||
| if (line.trim() === "") { | ||
| console.log(""); | ||
| } else { | ||
| console.log(`${String(counter).padStart(paddingSize)} ${line}`); | ||
| counter++; | ||
| } | ||
| } else { | ||
| console.log(line); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| program | ||
| .name("mycat") | ||
| .description("A custom implementation of the cat command") | ||
| .argument("<files...>", "files or wildcard patterns") | ||
| .option("-n, --number-all", "number all lines") | ||
| .option("-b, --number-nonempty", "number non-empty lines") | ||
| .action((patterns, options) => { | ||
| let allFiles = []; | ||
|
|
||
| patterns.forEach((p) => { | ||
| allFiles = allFiles.concat(expandWildcard(p)); | ||
| }); | ||
|
|
||
| allFiles.forEach((file) => printFile(file, options)); | ||
| }); | ||
|
|
||
| program.parse(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| #!/usr/bin/env node | ||
| const { program } = require("commander"); | ||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
|
|
||
| function listDirectory(dir, options) { | ||
| try { | ||
| const stats = fs.statSync(dir); | ||
|
|
||
| if (stats.isFile()) { | ||
| console.log(dir); | ||
| return; | ||
| } | ||
| } catch (e) { | ||
| console.error(`ls: cannot access '${dir}': No such file or directory`); | ||
| return; | ||
| } | ||
|
|
||
| let entries; | ||
|
|
||
| try { | ||
| entries = fs.readdirSync(dir, { withFileTypes: true }); | ||
| } catch (e) { | ||
| console.error(`ls: cannot access '${dir}': No such file or directory`); | ||
| return; | ||
| } | ||
|
|
||
| let names = entries.map(e => e.name); | ||
|
|
||
| if (options.all) { | ||
| names.unshift(".", ".."); | ||
| } else { | ||
| names = names.filter(name => !name.startsWith(".")); | ||
| } | ||
|
|
||
| names.sort(); | ||
| names.forEach(name => console.log(name)); | ||
| } | ||
|
|
||
| program | ||
| .name("myls") | ||
| .description("Custom implementation of ls") | ||
| .option("-1", "list one file per line (default in our version)") | ||
|
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. if
Author
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. @LonMcGregor thank you .Good point — since this implementation only supports -1 output, I’ve made the -1 flag required rather than treating it as a default. This makes the behaviour explicit and testable. 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. Good work making these changes. You've made this now a required flag. What does this mean? If the flag is always required and always active, how would you test the alternative, of having the output on a single line?
Author
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. @LonMcGregor, you’re right — making the flag always required means it’s always active, so I wouldn’t be able to test the alternative output (without the single-line format). I updated the implementation so the |
||
| .option("-a, --all", "include hidden files") | ||
| .argument("[dir]", "directory to list", ".") | ||
| .action((dir, options) => { | ||
| listDirectory(dir, options); | ||
| }); | ||
|
|
||
| program.parse(); | ||
|
LonMcGregor marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| #!/usr/bin/env node | ||
| const { program } = require("commander"); | ||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
|
|
||
| function expandWildcard(pattern) { | ||
| const dir = path.dirname(pattern); | ||
| const base = path.basename(pattern); | ||
|
|
||
| if (!base.includes("*")) return [pattern]; | ||
|
|
||
| let files; | ||
| try { | ||
| files = fs.readdirSync(dir); | ||
| } catch { | ||
| console.error(`wc: ${pattern}: No such directory`); | ||
| return []; | ||
| } | ||
|
|
||
| const regex = new RegExp("^" + base.replace(/\*/g, ".*") + "$"); | ||
| return files | ||
| .filter(f => regex.test(f)) | ||
| .map(f => path.join(dir, f)); | ||
| } | ||
|
|
||
| function countLines(text) { | ||
| if (text === "") return 0; | ||
| const matches = text.match(/\n/g) || []; | ||
| return text.endsWith("\n") ? matches.length : matches.length + 1; | ||
| } | ||
|
|
||
| function countWords(text) { | ||
| return text.split(/\s+/).filter(Boolean).length; | ||
| } | ||
|
|
||
| function countChars(text) { | ||
| return Buffer.byteLength(text, "utf-8"); | ||
| } | ||
|
|
||
| function wcFile(filename, options) { | ||
| let text; | ||
| try { | ||
| text = fs.readFileSync(filename, "utf-8"); | ||
| } catch { | ||
| console.error(`wc: ${filename}: No such file`); | ||
| return null; | ||
| } | ||
|
|
||
| const lineCount = countLines(text); | ||
| const wordCount = countWords(text); | ||
| const charCount = countChars(text); | ||
|
|
||
| let output; | ||
| const paddingSize = 7; | ||
| if (options.lines && !options.words && !options.chars) output = `${lineCount} ${filename}`; | ||
| else if (options.words && !options.lines && !options.chars) output = `${wordCount} ${filename}`; | ||
| else if (options.chars && !options.lines && !options.words) output = `${charCount} ${filename}`; | ||
| else output = `${String(lineCount).padStart(paddingSize)} ${String(wordCount).padStart(paddingSize)} ${String(charCount).padStart(paddingSize)} ${filename}`; | ||
| console.log(output); | ||
|
|
||
| return { lines: lineCount, words: wordCount, chars: charCount }; | ||
| } | ||
|
|
||
| program | ||
| .name("mywc") | ||
| .description("Custom implementation of wc") | ||
| .option("-l, --lines", "count lines") | ||
| .option("-w, --words", "count words") | ||
| .option("-c, --chars", "count characters") | ||
| .argument("<files...>", "files or wildcard patterns") | ||
| .action((patterns, options) => { | ||
| let allFiles = []; | ||
| patterns.forEach(p => allFiles = allFiles.concat(expandWildcard(p))); | ||
|
|
||
| let totalLines = 0, totalWords = 0, totalChars = 0; | ||
|
|
||
| allFiles.forEach(file => { | ||
| const result = wcFile(file, options); | ||
| if (result) { | ||
| totalLines += result.lines; | ||
| totalWords += result.words; | ||
| totalChars += result.chars; | ||
| } | ||
| }); | ||
| const paddingSize = 7; | ||
| if (allFiles.length > 1) { | ||
| if (options.lines && !options.words && !options.chars) console.log(`${totalLines} total`); | ||
|
LonMcGregor marked this conversation as resolved.
Outdated
|
||
| else if (options.words && !options.lines && !options.chars) console.log(`${totalWords} total`); | ||
| else if (options.chars && !options.lines && !options.words) console.log(`${totalChars} total`); | ||
| else console.log( | ||
| `${String(totalLines).padStart(paddingSize)} ` + | ||
| `${String(totalWords).padStart(paddingSize)} ` + | ||
| `${String(totalChars).padStart(paddingSize)} total`); | ||
| } | ||
| }); | ||
|
|
||
| program.parse(); | ||
Uh oh!
There was an error while loading. Please reload this page.