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
56 changes: 56 additions & 0 deletions implement-shell-tools/cat/cat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { program } from "commander";
import process from "node:process";
import { promises as fs } from "node:fs";
import glob from "glob";

program
.name("cat")
.description("print the content of file")
.option("-n, --line-numbers", "Number the output lines, starting at 1")
.option(
"-b, --number-nonblank",
"Number non-empty output lines, overrides -n"
)
.argument("<paths...>", "The file path(s) to process"); // to support multiple file
program.parse();

const argv = program.args.flatMap((path) => glob.sync(path));

const options = program.opts();
if (argv.length === 0) {
console.error(`No file paths provided`); // to support more files
process.exit(1);
}

let lineCounter = 1;
function printLine(line, number = false) {
if (number && line.trim() !== "") {
const lineNumber = String(lineCounter++).padStart(6, " ");
console.log(`${lineNumber}\t${line}`);
} else {
console.log(line);
}
}
// Helper function to print totals for multiple files
function printTotal(total, label = "total") {
console.log(`${total} ${label}`);
}
for (const path of argv) {
try {
const content = await fs.readFile(path, "utf-8");
const lines = content.split(/\r?\n/);
if (lines.length && lines[lines.length - 1] === "") {
// Remove trailing empty line if it's just from the final newline
lines.pop();
}
// Determine if we need to number lines
const numberLines = options.numberNonblank || options.lineNumbers;

lines.forEach((line) => printLine(line, numberLines));

// Add a newline if the file didn't end with one
if (!content.endsWith("\n")) process.stdout.write("\n");
} catch (err) {
console.error(`cat: ${path}: ${err.message}`);
}
}
35 changes: 35 additions & 0 deletions implement-shell-tools/ls/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { program } from "commander";
import { promises as fs } from "node:fs";

program
.name("ls")
.description("Displays files in a directory")
.option("-1, --one", "Display each file on a new line")// for listing one file per line
.option("-a, --all", "Include hidden files")
.argument("[filepath]", "Directory to list files from (default: current)")

await program.parseAsync();

const opts = program.opts();
const args = program.args;

const path = args[0] || "."; //Use current directory if no path provided
let files ;
try {
files = await fs.readdir(path);
}catch (err) {
console.error(`ls: cannot access '${path}': ${err.message}`);
process.exit(1);

}

if (!opts.all) {
files = files.filter(file => !file.startsWith("."));// if path not provided use current directory
}

if (opts.one) {
files.forEach(file => console.log(file));
} else {
console.log(files.join(" "));
}

107 changes: 107 additions & 0 deletions implement-shell-tools/package-lock.json

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

7 changes: 7 additions & 0 deletions implement-shell-tools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "module",
"dependencies": {
"commander": "^14.0.0",
"glob": "^8.1.0"
}
}
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 @@
import { program } from "commander";
import { promises as fs } from "node:fs";
import pkg from "glob";
const { glob } = pkg;

program
.name("wc")
.description("Count lines, words, and bytes in files")
.option("-l, --lines", "Only print line counts")
.option("-w, --words", "Only print word counts")
.option("-c, --bytes", "Only print byte counts")
.argument("<files...>", "One or more files to process");
await program.parseAsync();

const files = program.args.flatMap((path) => glob.sync(path));
const options = program.opts();

function countContent(content) {
const lines = (content.match(/\n/g) || []).length;
const words = content.trim().split(/\s+/).filter(Boolean).length;
const bytes = Buffer.byteLength(content, "utf-8");
return { lines, words, bytes };
}
// Helper to print counts based on options
function printCounts({ lines, words, bytes }, label) {
if (options.lines) {
console.log(`${lines} ${label}`);
} else if (options.words) {
console.log(`${words} ${label}`);
} else if (options.bytes) {
console.log(`${bytes} ${label}`);
} else {
console.log(`${lines} ${words} ${bytes} ${label}`);
}
}

let totalLines = 0;
let totalWords = 0;
let totalBytes = 0;

for (const file of files) {
try {
const content = await fs.readFile(file, "utf-8");
const counts = countContent(content);

printCounts(counts, file);

totalLines += counts.lines;
totalWords += counts.words;
totalBytes += counts.bytes;
} catch (err) {
console.error(`wc: ${file}: ${err.message}`);
}
}

if (files.length > 1) {
printCounts(
{ lines: totalLines, words: totalWords, bytes: totalBytes },
"total"
);
}
Loading