Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@

prep
node_modules
.venv
2 changes: 2 additions & 0 deletions implement-shell-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
prep/
prep/
50 changes: 50 additions & 0 deletions implement-shell-tools/cat/cat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { program } from "commander";
import { promises as fs } from "node:fs";

//here we Configure the program: what flags and arguments it accepts
// .option("short, long", "description")
//The < > brackets mean "required"

program
.name("cat")
.description("concatenate and print files")
.option("-n, --number", "Number all output lines")
.option("-b, --numberNonBlank", "Number non-blank output lines")
.argument("<path...>", "The file paths to process");

// here Parse command line arguments (reads process.argv and interprets it)
program.parse();

// Shared counter across all files for -n and -b flags
let lineNumber = 0;


// Process each file
for (const path of program.args) {
const hasNumberFlag = program.opts().number; // True if user used -n flag
const shouldNumberNonBlank = program.opts().numberNonBlank;

//read the file for each argument
const content = await fs.readFile(path, "utf-8");
const lines = content.split("\n"); // Split into array of lines

// Output with or without line numbers
if (hasNumberFlag) {
// Add line numbers to each line
const numberedLines = lines.map((line) => {
lineNumber = lineNumber + 1;
return `${lineNumber} ${line}`; // Format: " 1 Hello"
});
console.log(numberedLines.join("\n")); // Join back with newlines
} else if (shouldNumberNonBlank) {

const numberedLines = lines.map((line) => {
return line.trim() === "" ? line : `${++lineNumber} ${line}`;
});
console.log(numberedLines.join("\n"));
} else {
// Just print the file as-is
console.log(content);
}
}

47 changes: 47 additions & 0 deletions implement-shell-tools/ls/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { program } from "commander";
import { promises as fs } from "node:fs";

//config the program
program
.name("ls")
.description("list directory contents")
.option("-1, --one", "Force output to be one entry per line")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You include the 1 option here, do you ever use it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I realized the flag wasn't doing anything because my output was always vertical. I’ve refactored it to be horizontal by default, so the -1 flag now correctly 'forces' the single-column output.

.option(
"-a, --all",
"shows all the files including the hidden ones which start with a dot"
)
.argument("[directory]", "Directory to list", "."); // "." means current directory

//interpret the program
program.parse();

// Get the directory argument (first argument in program.args array)
// If no argument provided, default to current directory "."
const directory = program.args[0] || ".";

//read the directory to get array of filenames
const files = await fs.readdir(directory);

//check for flags
const hasAflag = program.opts().all;
const hasOneFlag = program.opts().one;

// Filter the files array BEFORE looping
// If hasAflag is true, keep all files
// If hasAflag is false, remove files that start with "."

const fileToShow = hasAflag
? files
: files.filter(file => !file.startsWith("."))


if (hasOneFlag) {

for (const file of fileToShow) {
console.log(file);
}
} else {
// print horizontally
console.log(fileToShow.join(" "));
}

3 changes: 3 additions & 0 deletions implement-shell-tools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
66 changes: 66 additions & 0 deletions implement-shell-tools/wc/wc.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good implementation!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @LonMcGregor ,

I hope you had a lovely holiday.

Thanks for your patience with this! It took me a bit longer to get back to these changes as I had to step away for a week to complete my English and Life in the UK tests, which was immediately followed by the Christmas break.
I've now addressed all the feedback.

Happy New Year to you, and I look forward to your thoughts on the updates!

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { program } from "commander";
import { promises as fs } from "node:fs";

//config the program
program
.name("wc")
.description(
"The wc utility displays the number of lines, words, and bytes contained in each input file, or standard input"
)
.option("-l, --lines", "count lines only")
.option("-w, --words", "count words only")
.option("-c, --bytes", "count bytes only")
.argument("<path...>", "The file paths to process");

//interpret the program
program.parse();

//initialise totals
let totalLines = 0;
let totalWords = 0;
let totalBytes = 0;

//check for flags
const hasLineFlag = program.opts().lines;
const hasWordFlag = program.opts().words;
const hasBytesFlag = program.opts().bytes;

// create output format function to avoid repetition
function formatOutput(lines, words, bytes, path) {
if (hasLineFlag) {
console.log(`${lines} ${path}`);
} else if (hasWordFlag) {
console.log(`${words} ${path}`);
} else if (hasBytesFlag) {
console.log(`${bytes} ${path}`);
} else {
console.log(`${lines} ${words} ${bytes} ${path}`);
}
}

//process each file

for (const path of program.args) {
//read the file
const content = await fs.readFile(path, "utf-8");

//count lines
const lines = content.split("\n").length - 1;

//count words (split by any whitespace)
const words = content.split(/\s+/).filter((word) => word.length > 0).length;

//count bytes correctly especially important for non-ASCII characters
const bytes = Buffer.byteLength(content, "utf-8");

//Add to totals
totalLines += lines;
totalWords += words;
totalBytes += bytes;

formatOutput(lines, words, bytes, path);
}

if (program.args.length > 1) {
formatOutput(totalLines, totalWords, totalBytes, "total");
}
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "module",
"dependencies": {
"commander": "^14.0.2"
}
}