Skip to content
41 changes: 41 additions & 0 deletions implement-shell-tools/cat/my_cat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { promises as fs } from 'node:fs';
import { argv } from 'node:process';

const args = argv.slice(2); // skip node and script name

let showLineNumb = false;
let showNoBlankNumb = false
let file = '';


args.forEach(arg => {
if (arg === '-n') {
showLineNumb = true;
} else if (arg === '-b') {
showLineNumb = false;
showNoBlankNumb = true;
}else {
file = arg;

Choose a reason for hiding this comment

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

What happens if I want to pass multiple files in here? For example, using the * wildcard which expands to multiple files in the shell.

Your solution in wc seems a better approach

Choose a reason for hiding this comment

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

This seems to be fixed now - well done

}
});

try {
let content = await fs.readFile(file, 'utf-8');
const lines = content.split('\n');

if (showLineNumb) {
// Number all lines
content = lines.map((line, i) => `${i + 1}\t${line}`).join('\n');
}else if (showNoBlankNumb) {
// Number only non-blank lines
let counter = 1;
content = lines.map(line => {
if (line.trim() === '') return '';
return `${counter++}\t${line}`;
}).join('\n');
}

console.log (content)

Choose a reason for hiding this comment

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

Be careful of formatting your code the same way. Can you see why this line is inconsistent with the rest of the file?

} catch (err) {
console.error(`Cannot access '${file}': ${err.message}`);
}
29 changes: 29 additions & 0 deletions implement-shell-tools/ls/my_ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { readdirSync } from 'node:fs';
import { argv } from 'node:process';

const args = argv.slice(2); // skip node and script name

let showAll = false;
let dir = '.'; //set dir to current directory

// Parse args to check weather or not to show hidden files
//and ignore -1 because our output is already 1 per line
args.forEach(arg => {
if (arg === '-a') {
showAll = true;
} else if (arg !== '-1') {

Choose a reason for hiding this comment

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

This approach might cause problems in two cases:

  • What if the user does not want each output per line and wants it all on one line (like the original ls) app?
  • Assuming anything else is a directory might make it more difficult to add new options in the future.

Choose a reason for hiding this comment

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

This is fixed now - great

dir = arg; //assumes anything else is our target directory
}
});

try {
let files = readdirSync(dir); // Reads the contents of the dir synchronously (readdirSync)

if (!showAll) {
files = files.filter(file => !file.startsWith('.'));
}

files.forEach(file => console.log(file));
} catch (err) {
console.error(`Cannot access '${dir}': ${err.message}`);
}
89 changes: 89 additions & 0 deletions implement-shell-tools/wc/my_wc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { promises as fs } from 'node:fs';
import { argv } from 'node:process';

const args = argv.slice(2);

if (args.length === 0) {
console.error('Usage: node my_wc.mjs [-l] [-w] [-c] <file> [file2 ...]');
process.exit(1);
}

// Parse flags and files
const flags = {
l: false,
w: false,
c: false,
};

const files = [];

for (const arg of args) {
if (arg.startsWith('-') && arg.length > 1) {
for (const ch of arg.slice(1)) {
if (flags.hasOwnProperty(ch)) {
flags[ch] = true;
} else {
console.error(`Unknown option: -${ch}`);
process.exit(1);
}
}
} else {
files.push(arg);
}
}

if (files.length === 0) {
console.error('Error: no files specified.');
process.exit(1);
}

// If no flags specified, show all by default
const showLines = flags.l || (!flags.l && !flags.w && !flags.c);
const showWords = flags.w || (!flags.l && !flags.w && !flags.c);
const showBytes = flags.c || (!flags.l && !flags.w && !flags.c);

async function countFile(file) {
try {
const content = await fs.readFile(file, 'utf-8');
const lines = content.split('\n').filter(line => line.trim() !== '').length;
const words = content.trim().split(/\s+/).filter(Boolean).length;
const bytes = Buffer.byteLength(content, 'utf-8');

let output = '';
if (showLines) output += `${lines} `;
if (showWords) output += `${words} `;
if (showBytes) output += `${bytes} `;

output += file;

console.log(output.trim());

return { lines, words, bytes };
} catch (err) {
console.error(`Cannot access '${file}': ${err.message}`);
return null;
}
}

async function main() {
const counts = await Promise.all(files.map(countFile));

Choose a reason for hiding this comment

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

This is a very minor point, but when doing map we expect it to return a new array that is different from the input array. Instead of .map can you think of another array function that would make more sense to use here?

Choose a reason for hiding this comment

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

Good solution


const validCounts = counts.filter(c => c !== null);

if (validCounts.length > 1) {
const totalLines = validCounts.reduce((sum, c) => sum + c.lines, 0);
const totalWords = validCounts.reduce((sum, c) => sum + c.words, 0);
const totalBytes = validCounts.reduce((sum, c) => sum + c.bytes, 0);

let totalOutput = '';
if (showLines) totalOutput += `${totalLines} `;

Choose a reason for hiding this comment

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

There is some duplication here when you are printing totals and when you print single file output. Can you think of how to reduce this?

Choose a reason for hiding this comment

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

Great work

if (showWords) totalOutput += `${totalWords} `;
if (showBytes) totalOutput += `${totalBytes} `;

totalOutput += 'total';

console.log(totalOutput.trim());
}
}

main();