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;
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 variable 'lineNumber' is used to number lines either globally or only for non-empty lines, depending on the options. The name is accurate, but you might consider if the logic for incrementing and resetting (if needed) matches the behavior you want for '-n' and '-b' flags. For example, on macOS, '-n' resets the line number for each file, while on Linux it continues across files. Your implementation continues across files, which is fine as long as it matches your intended behavior.


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
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 line 12, you have a comment explaining that hidden files are skipped unless -a is specified. Since the code directly checks for the '-a' option and the file name starting with '.', this logic is already quite clear from the code itself. Do you think this comment adds much value, or could the code be written in a way that makes the intent even more obvious without needing a comment?

}
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
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 line 31, you mention that '-1' is the default behavior, so no action is needed. Since the code does not do anything for this case, and the logic is straightforward, do you think this comment is necessary? Sometimes, omitting comments for default or no-op cases can help keep the code cleaner.

} else if (arg === '-a') {
options.all = true;
} else {
directories = [arg];
}
Comment on lines +27 to +36
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 variable 'directories' is initialized as an array, but later in the argument parsing, it is reassigned to a single-element array containing only the last non-option argument. This could be confusing if you ever want to support multiple directories. The name 'directories' suggests it could hold more than one directory, but the logic only allows one. Would a name like 'directory' be more accurate for your current implementation?

});

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