From 3cb1e276b7b068019c0fec241901b0804ea28565 Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Fri, 10 Apr 2026 17:52:13 +0100 Subject: [PATCH 1/6] implementing shell tools exercise is compleat --- implement-shell-tools/cat/my-cat.sh | 42 +++++++++++++++++++++++++++ implement-shell-tools/ls/my-ls.sh | 34 ++++++++++++++++++++++ implement-shell-tools/wc/my-wc.sh | 45 +++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100755 implement-shell-tools/cat/my-cat.sh create mode 100755 implement-shell-tools/ls/my-ls.sh create mode 100755 implement-shell-tools/wc/my-wc.sh diff --git a/implement-shell-tools/cat/my-cat.sh b/implement-shell-tools/cat/my-cat.sh new file mode 100755 index 000000000..7b9700803 --- /dev/null +++ b/implement-shell-tools/cat/my-cat.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -euo pipefail + +number_lines=false +number_non_empty=false + +# check for flags +if [ "${1:-}" = "-n" ]; then + number_lines=true + shift +elif [ "${1:-}" = "-b" ]; then + number_non_empty=true + shift +fi + +count=1 + +# Loop through all files + +for file in "$@" +do + while IFS= read -r line + do + if [ "$number_lines" = true ]; then + echo "$count $line" + count=$((count+1)) + + else if [ "$number_non_empty" = true ]; then + if [ -n "$line" ]; then + echo "$count $line" + count=$((count+1)) + else + echo "" + fi + + else + echo "$line" + fi + fi + done < "$file" +done \ No newline at end of file diff --git a/implement-shell-tools/ls/my-ls.sh b/implement-shell-tools/ls/my-ls.sh new file mode 100755 index 000000000..b44cbf072 --- /dev/null +++ b/implement-shell-tools/ls/my-ls.sh @@ -0,0 +1,34 @@ + #!/bin/bash + set -euo pipefail + +show_all=false + +# handle -a +if [ "${1:-}" = "-a" ]; then + show_all=true + shift +fi + +# directory (default current) + dir="." + + if [ "${1:-}" != "" ]; then + dir="$1" + fi + + #choose pattern + if [ "$show_all" = true ]; then + files="$dir"/.* + else + files="$dir"/* + fi + # loop + for file in $files + do + name="$(basename "$file")" + + if [ "$name" = "." ] || [ "$name" = ".." ]; then + continue + fi + echo "$name" + done \ No newline at end of file diff --git a/implement-shell-tools/wc/my-wc.sh b/implement-shell-tools/wc/my-wc.sh new file mode 100755 index 000000000..067b2019f --- /dev/null +++ b/implement-shell-tools/wc/my-wc.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -euo pipefail + +flag="all" + +if [ "${1:-}" = "-l" ]; then + flag="l" + shift +elif [ "${1:-}" = "-w" ]; then + flag="w" + shift +elif [ "${1:-}" = "-c" ]; then + flag="c" + shift +fi + +for file in "$@" +do + if [ -d "$file" ]; then + continue + fi + + lines=0 + words=0 + chars=0 + + while IFS= read -r line + do + lines=$((lines + 1)) + words=$((words + $(echo "$line" | wc -w))) + chars=$((chars + ${#line} + 1)) + done <"$file" + + if [ "$flag" = "l" ]; then + echo "$lines $file" + elif [ "$flag" = "w" ]; then + echo "$words $file" + elif [ "$flag" = "c" ]; then + echo "$chars $file" + else + echo "$lines $words $chars $file" + fi +done + From 5ede2e8b3240e8d396a77b632287cb1fd0a0b427 Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Fri, 24 Apr 2026 14:23:50 +0100 Subject: [PATCH 2/6] implemented cat, ls, wc with java script --- implement-shell-tools/cat/my-cat.js | 41 ++++++++++++++++++ implement-shell-tools/ls/my-ls.js | 41 ++++++++++++++++++ implement-shell-tools/wc/my-wc.js | 67 +++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100755 implement-shell-tools/cat/my-cat.js create mode 100755 implement-shell-tools/ls/my-ls.js create mode 100755 implement-shell-tools/wc/my-wc.js diff --git a/implement-shell-tools/cat/my-cat.js b/implement-shell-tools/cat/my-cat.js new file mode 100755 index 000000000..47e1444de --- /dev/null +++ b/implement-shell-tools/cat/my-cat.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +const fs = require("fs"); + +const args = process.argv.slice(2); + +let numberLines = false; +let numberNonEmpty = false; + +if (args[0] === "-n") { + numberLines = true; + args.shift(); +} else if (args[0] === "-b") { + numberNonEmpty = true; + args.shift(); +} +let count = 1; + +args.forEach((file) => { + const content = fs.readFileSync(file, "utf-8"); + const lines = content.split(/\r?\n/); + + + if (numberLines){ + lines.forEach((line) => { + console.log(`${count} ${line}`); + count++; + }); + } else if (numberNonEmpty){ + lines.forEach((line) => { + if (line !== "") { + console.log(`${count} ${line}`); + count++; + } else { + console.log(""); + } + }); +} else { +console.log(content); + } +}); diff --git a/implement-shell-tools/ls/my-ls.js b/implement-shell-tools/ls/my-ls.js new file mode 100755 index 000000000..8c3ecd812 --- /dev/null +++ b/implement-shell-tools/ls/my-ls.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +const fs = require("node:fs"); +const path = require("node:path"); + +function parseArgs(args) { + let showAll = false; + let targetDir = "."; + + for (const arg of args) { + if (arg === "-a") { + showAll = true; + } else if (!arg.startsWith("-")) { + targetDir = arg; + } + } + + return { showAll, targetDir }; +} + +function listDirectory(dirPath, showAll) { + let files = fs.readdirSync(dirPath); + + if (!showAll) { + files = files.filter(file => !file.startsWith(".")); + } + + return files.sort(); +} + +function main() { + const args = process.argv.slice(2); + const { showAll, targetDir } = parseArgs(args); + + const files = listDirectory(targetDir, showAll); + console.log(files.join("\n")); +} + +main(); + + diff --git a/implement-shell-tools/wc/my-wc.js b/implement-shell-tools/wc/my-wc.js new file mode 100755 index 000000000..ccd2658bf --- /dev/null +++ b/implement-shell-tools/wc/my-wc.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node + +const fs = require("node:fs"); + +function countFile(filePath) { + const content = fs.readFileSync(filePath, "utf8"); + + const lines = content.split("\n").length - 1; + const words = content.trim() ? content.trim().split(/\s+/).length : 0; + const chars = Buffer.byteLength(content, "utf8"); + + return { lines, words, chars }; +} + +function main() { + const args = process.argv.slice(2); + + let flag = null; + let files = []; + + for (const arg of args) { + if (arg === "-l" || arg === "-w" || arg === "-c") { + flag = arg; + } else { + files.push(arg); + } + } + + let totalLines = 0; + let totalWords = 0; + let totalChars = 0; + + for (const file of files) { + const { lines, words, chars } = countFile(file); + + totalLines += lines; + totalWords += words; + totalChars += chars; + + if (flag === "-l") { + console.log(`${lines} ${file}`); + } else if (flag === "-w") { + console.log(`${words} ${file}`); + } else if (flag === "-c") { + console.log(`${chars} ${file}`); + } else { + console.log(`${lines} ${words} ${chars} ${file}`); + } + } + + if (files.length > 1) { + if (flag === "-l") { + console.log(`${totalLines} total`); + } else if (flag === "-w") { + console.log(`${totalWords} total`); + } else if (flag === "-c") { + console.log(`${totalChars} total`); + } else { + console.log(`${totalLines} ${totalWords} ${totalChars} total`); + } + } +} + +main(); + + + From 82f5cc276fe5a02fbf716b3cb9003ac3facdef6d Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Wed, 3 Jun 2026 09:38:04 +0100 Subject: [PATCH 3/6] some changes --- .gitignore | 1 + implement-shell-tools/cat/my-cat.js | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 3c3629e64..5f384a0ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +*/venv \ No newline at end of file diff --git a/implement-shell-tools/cat/my-cat.js b/implement-shell-tools/cat/my-cat.js index 47e1444de..95c75702c 100755 --- a/implement-shell-tools/cat/my-cat.js +++ b/implement-shell-tools/cat/my-cat.js @@ -6,15 +6,17 @@ const args = process.argv.slice(2); let numberLines = false; let numberNonEmpty = false; +let files = []; -if (args[0] === "-n") { +for (const arg of args) { +if (arg === "-n") { numberLines = true; - args.shift(); -} else if (args[0] === "-b") { +} else if (arg === "-b") { numberNonEmpty = true; - args.shift(); -} -let count = 1; +} else { + files.push(arg); +} +}; args.forEach((file) => { const content = fs.readFileSync(file, "utf-8"); From bcc13af75129a2c7031f830534190213053c20c3 Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Tue, 9 Jun 2026 13:04:25 +0100 Subject: [PATCH 4/6] moving common printing logic into a single loop --- implement-shell-tools/cat/README.md | 10 ++++---- implement-shell-tools/cat/my-cat.js | 38 +++++++++++++---------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/implement-shell-tools/cat/README.md b/implement-shell-tools/cat/README.md index 7284a5e67..71a08ca9b 100644 --- a/implement-shell-tools/cat/README.md +++ b/implement-shell-tools/cat/README.md @@ -6,11 +6,11 @@ Your task is to implement your own version of `cat`. It must act the same as `cat` would, if run from the directory containing this README.md file, for the following command lines: -* `cat sample-files/1.txt` -* `cat -n sample-files/1.txt` -* `cat sample-files/*.txt` -* `cat -n sample-files/*.txt` -* `cat -b sample-files/3.txt` +- `cat sample-files/1.txt` +- `cat -n sample-files/1.txt` +- `cat sample-files/*.txt` +- `cat -n sample-files/*.txt` +- `cat -b sample-files/3.txt` Matching any additional behaviours or flags are optional stretch goals. diff --git a/implement-shell-tools/cat/my-cat.js b/implement-shell-tools/cat/my-cat.js index 95c75702c..f1763666b 100755 --- a/implement-shell-tools/cat/my-cat.js +++ b/implement-shell-tools/cat/my-cat.js @@ -8,36 +8,32 @@ let numberLines = false; let numberNonEmpty = false; let files = []; +//Parse arguments for (const arg of args) { -if (arg === "-n") { - numberLines = true; -} else if (arg === "-b") { - numberNonEmpty = true; -} else { - files.push(arg); -} -}; + if (arg === "-n") { + numberLines = true; + } else if (arg === "-b") { + numberNonEmpty = true; + } else { + files.push(arg); + } +} +//Process each files args.forEach((file) => { const content = fs.readFileSync(file, "utf-8"); const lines = content.split(/\r?\n/); - - if (numberLines){ - lines.forEach((line) => { - console.log(`${count} ${line}`); - count++; - }); - } else if (numberNonEmpty){ - lines.forEach((line) => { - if (line !== "") { + let count = 1; + + lines.forEach((line) => { + const shouldNumber = numberLines || (numberNonEmpty && line !== ""); + + if (shouldNumber) { console.log(`${count} ${line}`); count++; } else { - console.log(""); + console.log(line); } }); -} else { -console.log(content); - } }); From 6d08c59d2d3874c1f7c0c560fa6cf673705bca73 Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Tue, 9 Jun 2026 14:19:55 +0100 Subject: [PATCH 5/6] changed the sorting of outcome to match real ls --- implement-shell-tools/ls/my-ls.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/implement-shell-tools/ls/my-ls.js b/implement-shell-tools/ls/my-ls.js index 8c3ecd812..15cf49f7a 100755 --- a/implement-shell-tools/ls/my-ls.js +++ b/implement-shell-tools/ls/my-ls.js @@ -21,11 +21,29 @@ function parseArgs(args) { function listDirectory(dirPath, showAll) { let files = fs.readdirSync(dirPath); + let hidden = []; + let normal = files; + if (!showAll) { - files = files.filter(file => !file.startsWith(".")); + normal = files.filter(file => !file.startsWith(".")); + } else { + hidden = files.filter(file => file.startsWith(".")); + normal = files.filter(file => !file.startsWith(".")); + + } + + normal.sort(); + hidden.sort(); + + let result = []; + + if (showAll) { + result.push(".",".."); } + result.push (...normal, ...hidden); - return files.sort(); + return result; + } function main() { From d0e165cffcdcdf2b6c013f7f74ec0421e8b01e8a Mon Sep 17 00:00:00 2001 From: HilolaRustamova Date: Wed, 10 Jun 2026 11:12:55 +0100 Subject: [PATCH 6/6] fixed the wc implementation to support multiple flags at the same time. --- implement-shell-tools/wc/my-wc.js | 70 +++++++++++++++++++------------ 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/implement-shell-tools/wc/my-wc.js b/implement-shell-tools/wc/my-wc.js index ccd2658bf..537e3a47a 100755 --- a/implement-shell-tools/wc/my-wc.js +++ b/implement-shell-tools/wc/my-wc.js @@ -3,6 +3,12 @@ const fs = require("node:fs"); function countFile(filePath) { + const stats = fs.statSync(filePath); + + if (!stats.isFile()) { + console.warn(`${filePath} is not a file, skipping`); + return null; + } const content = fs.readFileSync(filePath, "utf8"); const lines = content.split("\n").length - 1; @@ -15,49 +21,59 @@ function countFile(filePath) { function main() { const args = process.argv.slice(2); - let flag = null; + let flags = []; let files = []; + // 1 Parse args for (const arg of args) { if (arg === "-l" || arg === "-w" || arg === "-c") { - flag = arg; + flags.push(arg); } else { files.push(arg); } } + //2 Helper function that decides what to print + + function formatOutput(counts, files) { + const parts = []; + + // if no flags show everything + const showAll = flags.length === 0; + + if (showAll || flags.includes("-l"))parts.push(counts.lines); + if (showAll || flags.includes("-w"))parts.push(counts.words); + if (showAll || flags.includes("-c"))parts.push(counts.chars); + + parts.push(files); + + return parts.join(" "); + } + // 3 totals let totalLines = 0; let totalWords = 0; let totalChars = 0; + + // 4 per-file output for (const file of files) { - const { lines, words, chars } = countFile(file); - - totalLines += lines; - totalWords += words; - totalChars += chars; - - if (flag === "-l") { - console.log(`${lines} ${file}`); - } else if (flag === "-w") { - console.log(`${words} ${file}`); - } else if (flag === "-c") { - console.log(`${chars} ${file}`); - } else { - console.log(`${lines} ${words} ${chars} ${file}`); - } - } + const counts = countFile(file); + if (!counts) continue; + totalLines += counts.lines; + totalWords += counts.words; + totalChars += counts.chars; + + console.log(formatOutput(counts, files)); + } + // 5 Total output (only if multiple files) if (files.length > 1) { - if (flag === "-l") { - console.log(`${totalLines} total`); - } else if (flag === "-w") { - console.log(`${totalWords} total`); - } else if (flag === "-c") { - console.log(`${totalChars} total`); - } else { - console.log(`${totalLines} ${totalWords} ${totalChars} total`); - } + const totalCounts = { + lines: totalLines, + words: totalWords, + chars: totalChars, + }; + console.log(formatOutput(totalCounts,"total")); } }