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/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 new file mode 100755 index 000000000..f1763666b --- /dev/null +++ b/implement-shell-tools/cat/my-cat.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +const fs = require("fs"); + +const args = process.argv.slice(2); + +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); + } +} + +//Process each files +args.forEach((file) => { + const content = fs.readFileSync(file, "utf-8"); + const lines = content.split(/\r?\n/); + + let count = 1; + + lines.forEach((line) => { + const shouldNumber = numberLines || (numberNonEmpty && line !== ""); + + if (shouldNumber) { + console.log(`${count} ${line}`); + count++; + } else { + console.log(line); + } + }); +}); 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.js b/implement-shell-tools/ls/my-ls.js new file mode 100755 index 000000000..15cf49f7a --- /dev/null +++ b/implement-shell-tools/ls/my-ls.js @@ -0,0 +1,59 @@ +#!/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); + + let hidden = []; + let normal = files; + + if (!showAll) { + 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 result; + +} + +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/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.js b/implement-shell-tools/wc/my-wc.js new file mode 100755 index 000000000..537e3a47a --- /dev/null +++ b/implement-shell-tools/wc/my-wc.js @@ -0,0 +1,83 @@ +#!/usr/bin/env node + +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; + 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 flags = []; + let files = []; + + // 1 Parse args + for (const arg of args) { + if (arg === "-l" || arg === "-w" || arg === "-c") { + 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 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) { + const totalCounts = { + lines: totalLines, + words: totalWords, + chars: totalChars, + }; + console.log(formatOutput(totalCounts,"total")); + } +} + +main(); + + + 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 +