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
60 changes: 60 additions & 0 deletions implement-shell-tools/cat/cat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

function cat(files, options) {
let lineNumber = 1;

files.forEach((file) => {
const filePath = path.resolve(file);

try {
const data = fs.readFileSync(filePath, 'utf8');
const lines = data.split('\n');

lines.forEach((line) => {
if (options.numberNonEmpty && line.trim()) {
console.log(`${lineNumber}\t${line}`);
lineNumber++;
} else if (options.numberLines) {
console.log(`${lineNumber}\t${line}`);
lineNumber++;
} else {
console.log(line);
}
});
} catch (err) {
console.error(`cat: ${file}: No such file or directory`);
}
});
}

function main() {
const args = process.argv.slice(2);
const options = {
numberLines: false,
numberNonEmpty: false,
};

const files = [];

args.forEach((arg) => {
if (arg === '-n') {
options.numberLines = true;
} else if (arg === '-b') {
options.numberNonEmpty = true;
} else {
files.push(arg);
}
});

if (files.length === 0) {
console.error('Usage: node cat.js [-n | -b] <file>...');
process.exit(1);
}

cat(files, options);
}

main();
44 changes: 44 additions & 0 deletions implement-shell-tools/ls/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

function listFiles(directory, options) {
try {
const files = fs.readdirSync(directory, { withFileTypes: true });

files.forEach((file) => {
if (!options.all && file.name.startsWith('.')) {
return; // Skip hidden files unless -a is specified
}
Comment on lines +11 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On this line the code comment explains that hidden files are skipped unless -a is specified. Because the if condition (!options.all && file.name.startsWith('.')) already expresses that behavior quite clearly, the comment is mostly repeating what the code says. When comments just restate the obvious logic, they can become noise and may fall out of sync with the code later.

How might you decide when a comment is adding extra context ("why" we do something) versus just narrating the "what" that the code already shows? In this case, could the code be clear enough without the comment, or would renaming options.all to something like includeHidden make it even more self-explanatory so no comment is needed?

console.log(file.name);
});
} catch (err) {
console.error(`ls: cannot access '${directory}': No such file or directory`);
}
}

function main() {
const args = process.argv.slice(2);
const options = {
all: false,
};

let directories = ['.'];

args.forEach((arg) => {
if (arg === '-1') {
// -1 is the default behavior, so no action needed
} else if (arg === '-a') {
Comment on lines +30 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The comment here explains that -1 is the default behavior and therefore no action is taken in the if block. Since the block is intentionally empty, the comment is trying to justify that, but it also mirrors the logic in a way the code could potentially express on its own.

One thing to think about: could the intent be made clear enough through the structure of the code (for example, by handling -1 together with other flags in a way that doesn't require a no-op branch), so that an explanatory comment becomes unnecessary? When you see yourself writing comments to explain an empty or no-op branch, is there a way to simplify or restructure the code so its intent is obvious without extra text?

options.all = true;
} else {
directories = [arg];
}
});
Comment on lines +27 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In main, directories is initialized as an array (['.']), and its name suggests you may handle multiple directories. However, in the argument parsing you overwrite it with a new single-element array (directories = [arg];) instead of accumulating, so despite the plural name only one directory is ever kept. For someone reading the code, the plural name might suggest multi-directory support that is not actually there. Would a name that better reflects the current behavior (single directory) or, alternatively, changing the parsing to truly support multiple directories, make it clearer what the code is doing?


directories.forEach((directory) => {
listFiles(directory, options);
});
}

main();
61 changes: 61 additions & 0 deletions implement-shell-tools/wc/wc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

function countFile(filePath, options) {
try {
const data = fs.readFileSync(filePath, 'utf8');

const lines = data.split('\n').length;
const words = data.split(/\s+/).filter(Boolean).length;
const bytes = Buffer.byteLength(data, 'utf8');

if (options.lines) {
console.log(`${lines}\t${filePath}`);
} else if (options.words) {
console.log(`${words}\t${filePath}`);
} else if (options.bytes) {
console.log(`${bytes}\t${filePath}`);
} else {
console.log(`${lines}\t${words}\t${bytes}\t${filePath}`);
}
} catch (err) {
console.error(`wc: ${filePath}: No such file or directory`);
}
}

function main() {
const args = process.argv.slice(2);
const options = {
lines: false,
words: false,
bytes: false,
};

const files = [];

args.forEach((arg) => {
if (arg === '-l') {
options.lines = true;
} else if (arg === '-w') {
options.words = true;
} else if (arg === '-c') {
options.bytes = true;
} else {
files.push(arg);
}
});

if (files.length === 0) {
console.error('Usage: wc [-l | -w | -c] <file>...');
process.exit(1);
}

files.forEach((file) => {
const filePath = path.resolve(file);
countFile(filePath, options);
});
}

main();
Loading