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
48 changes: 48 additions & 0 deletions implement-shell-tools/cat/cat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const fs = require("fs");

const args = process.argv.slice(2);

let numberAllLines = false;
let numberNonBlankLines = false;
let startIndex = 0;
let lineNumber = 1;

// Check for flags
if (args[0] === "-n") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Flags are only checked at args[0]. That covers the required command lines - but ask yourself: what would happen with -n file1 file2, or a flag that isn't first?

numberAllLines = true;
startIndex = 1;
} else if (args[0] === "-b") {
numberNonBlankLines = true;
startIndex = 1;
}

// Read each file
for (let i = startIndex; i < args.length; i++) {
let fileName = args[i];

try {
let content = fs.readFileSync(fileName, "utf8");

let lines = content.split("\n");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If a file ends in \n, what's the last element of content.split("\n")? Combined with console.log adding its own newline (line 42), how does your output compare to cat sample-files/1.txt - same number of lines, or one extra?


for (let j = 0; j < lines.length; j++) {
let line = lines[j];

if (numberAllLines) {
console.log(lineNumber + " " + line);
lineNumber++;
} else if (numberNonBlankLines) {
if (line.trim() === "") {
console.log(line);
} else {
console.log(lineNumber + " " + line);
lineNumber++;
}
} else {
console.log(line);
}
}
} catch (error) {
console.log("Cannot read file: " + fileName);
}
}
49 changes: 49 additions & 0 deletions implement-shell-tools/ls/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const fs = require("fs");

const args = process.argv.slice(2);

let onePerLine = false;
let showHidden = false;
let path = "."; // Current directory by default

// Check for flags
for (let i = 0; i < args.length; i++) {
if (args[i] === "-1") {
onePerLine = true;
} else if (args[i] === "-a") {
showHidden = true;
} else {
path = args[i];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When you run ls sample-files/*, the shell passes many paths as separate arguments. Each loop does path = args[i] - after the loop, how many of those paths are left? What does the real ls show for a glob like that, and how could you keep all of them rather than just one?

}
}

try {
// Check if the path is a file
if (fs.statSync(path).isFile()) {
console.log(path);
} else {
let files = fs.readdirSync(path);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

readdirSync returns entries in whatever order the filesystem gives. Does the real ls do the same, or does it guarantee a particular order? What one call would make yours match?


// Print each file
for (let i = 0; i < files.length; i++) {
let file = files[i];

// Skip hidden files unless -a is used
if (!showHidden && file.startsWith(".")) {
continue;
}

if (onePerLine) {
console.log(file);
} else {
process.stdout.write(file + " ");
}
}

if (!onePerLine) {
console.log();
}
}
} catch (error) {
console.log("Cannot access: " + path);
}
68 changes: 68 additions & 0 deletions implement-shell-tools/wc/wc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const fs = require("fs");

// Get arguments after "node wc.js"
const args = process.argv.slice(2);

let countLines = false;
let countWords = false;
let countBytes = false;

let files = [];

// Read arguments
for (let arg of args) {
if (arg === "-l") {
countLines = true;
} else if (arg === "-w") {
countWords = true;
} else if (arg === "-c") {
countBytes = true;
} else {
files.push(arg);
}
}

// If no flags are given, wc shows all three
if (!countLines && !countWords && !countBytes) {
countLines = true;
countWords = true;
countBytes = true;
}

// Function to count one file
function countFile(fileName) {
const content = fs.readFileSync(fileName, "utf8");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cat.js wraps its readFileSync in try/catch, but this one doesn't - and sample-files/* includes the dir subdirectory. What happens when readFileSync is handed a directory, and what does that do to the files listed after it?


let lines = content.split("\n").length - 1;

let words = content
.trim()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice — trim().split(/\s+/).filter(...) correctly returns 0 words for an empty file, an edge case that trips a lot of people up.

.split(/\s+/)
.filter((word) => word.length > 0).length;

let bytes = Buffer.byteLength(content);

let result = "";

if (countLines) {
result += lines + " ";
}

if (countWords) {
result += words + " ";
}

if (countBytes) {
result += bytes + " ";
}

result += fileName;

console.log(result);
}

// Run wc for every file

for (let file of files) {
countFile(file);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Try the real wc sample-files/* with several files — what appears on the last line that your output doesn't have yet? Where in this loop could you accumulate the counts to produce that?

}
Loading