From 8fc4440150ce84c9cc3f5ec13d50beb58a5b65bc Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:58:00 +0200 Subject: Rebuild the generic packs, add shell and sysadmin, fix short race passages The packs were not proper. An audit found 86 of 253 items (33%) carrying an attribution that just restated the category - prose *about* a topic with an invented source, which is the same fault the movies pack had. Six of thirteen packs were mostly that: technology had 15 items and one distinct attribution. - science, technology, history, nature and business are now sourced quotes with real attributions: Feynman, Curie, Hopper, Dijkstra, Lincoln, Carson, Drucker, Goodhart. 82 items, all attributed to a person or a work. - general is original factual prose, so it now carries no attribution at all rather than claiming "General knowledge" as a source. merge_packs no longer invents one from the pack's filename. - Four explanatory passages in philosophy lost their "Philosophy" attribution for the same reason. - One duplicated passage removed. Generic attributions: 86/253 before, 0/349 now. New technical packs - shell: 24 awk, sed and pipeline drills, each explaining what the line does - field splitting, associative arrays, !seen[$0]++, process substitution, xargs -0, strict mode. - sysadmin: 24 operational one-liners across systemd, disk, processes, network, permissions, SSH, backup and containers. - programming grew to 37 and hacking to 30, with git bisect, window functions, EXPLAIN ANALYZE, certificate transparency and capability audits. - All 115 technical drills carry an explanation. Race passage length - Multiplayer drew from the same pool as single player, so a race could land on a 22-character quote and be over before anyone had their hands in position. Races now require 120 characters; the filter is applied when the pool is loaded, not to the packs, since a short quote is fine to type alone. For reference, TypeRacer organises by difficulty and language rather than topic: one default English pool of ~11,900 texts plus per-language universes and specials (accuracy, repeat, easytexts, anime). Their scale comes from user submission with moderation, which is still the feature this does not have. --- data/packs/shell.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 data/packs/shell.json (limited to 'data/packs/shell.json') diff --git a/data/packs/shell.json b/data/packs/shell.json new file mode 100644 index 0000000..3545ef9 --- /dev/null +++ b/data/packs/shell.json @@ -0,0 +1,26 @@ +[ + {"category":"shell","language":"shell","attribution":"awk","explanation":"awk splits each line into fields on whitespace. $1 is the first, $NF the last - so this prints the first and last column of every line.","content":"awk '{print $1, $NF}' access.log"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"-F sets the field separator. This reads /etc/passwd and prints the username and shell, which are fields 1 and 7.","content":"awk -F: '{print $1, $7}' /etc/passwd"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"A pattern before the block acts as a filter: only lines whose 9th field is 404 are printed. No grep needed.","content":"awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"Associative arrays are awk's real power. This sums bytes per IP in one pass, then END prints the totals once input is exhausted.","content":"awk '{bytes[$1] += $10} END {for (ip in bytes) print bytes[ip], ip}' access.log | sort -rn | head"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"NR is the current line number, NF the field count. This finds malformed rows: any line that does not have exactly three fields.","content":"awk -F, 'NF != 3 {print NR\": \"$0}' data.csv"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"Deduplicate without sorting, preserving original order. The array records what has been seen; !seen[$0]++ is true only the first time.","content":"awk '!seen[$0]++' file.txt"}, + {"category":"shell","language":"shell","attribution":"awk","explanation":"Sums a column and prints the average. END runs after the last line, so NR is the total row count by then.","content":"awk '{sum += $3} END {print sum, sum/NR}' numbers.txt"}, + {"category":"shell","language":"shell","attribution":"sed","explanation":"Substitute in place. The g flag replaces every match on a line, not just the first; -i writes the file rather than printing.","content":"sed -i 's/localhost/127.0.0.1/g' config.ini"}, + {"category":"shell","language":"shell","attribution":"sed","explanation":"Prints only lines 10 to 20. -n suppresses the default print, and p prints the range that matched.","content":"sed -n '10,20p' large.log"}, + {"category":"shell","language":"shell","attribution":"sed","explanation":"Deletes comments and blank lines, which is how you read a config that is mostly documentation.","content":"sed -e 's/#.*//' -e '/^[[:space:]]*$/d' /etc/ssh/sshd_config"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"The classic word-frequency pipeline: split to one word per line, fold case, sort so duplicates are adjacent, count runs, order by count.","content":"tr -cs '[:alpha:]' '\\n' < book.txt | tr 'A-Z' 'a-z' | sort | uniq -c | sort -rn | head -20"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"uniq only collapses adjacent duplicates, which is why sort comes first. -c counts, and the second sort orders by that count.","content":"cut -d' ' -f1 access.log | sort | uniq -c | sort -rn | head"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"xargs turns a stream of names into arguments. -0 with -print0 is what makes filenames containing spaces safe.","content":"find . -name '*.log' -print0 | xargs -0 grep -l 'ERROR'"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"Runs a command per input line in parallel. -P4 keeps four running at once, and -I{} places each name where the braces are.","content":"ls *.png | xargs -P4 -I{} convert {} -resize 50% small/{}"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"Process substitution gives two commands to diff as if they were files, without writing either to disk.","content":"diff <(sort a.txt) <(sort b.txt)"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"tee writes to a file and passes the stream on, so you can log and keep processing in the same pipeline.","content":"make 2>&1 | tee build.log | grep -E 'error|warning'"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"comm compares two sorted files by column: -13 shows only lines unique to the second, which is the set that was added.","content":"comm -13 <(sort old.txt) <(sort new.txt)"}, + {"category":"shell","language":"shell","attribution":"Pipelines","explanation":"jq selects and reshapes JSON. -r prints raw strings, so the output is usable by the next command rather than quoted.","content":"curl -s https://api.example.com/users | jq -r '.[] | select(.active) | .email'"}, + {"category":"shell","language":"shell","attribution":"Text processing","explanation":"grep -o prints only the match, one per line, which turns a search into a stream you can count.","content":"grep -oE '[0-9]{1,3}(\\.[0-9]{1,3}){3}' access.log | sort -u | wc -l"}, + {"category":"shell","language":"shell","attribution":"Text processing","explanation":"paste joins lines side by side; -s -d joins them all into one line with the chosen separator.","content":"cut -f2 data.tsv | paste -sd, -"}, + {"category":"shell","language":"shell","attribution":"Text processing","explanation":"column formats whitespace-separated input into aligned columns, which makes an unreadable log readable.","content":"mount | column -t | grep -v tmpfs"}, + {"category":"shell","language":"shell","attribution":"Shell safety","explanation":"The unofficial strict mode: exit on error, exit on undefined variable, and fail a pipeline if any stage fails rather than only the last.","content":"set -euo pipefail"}, + {"category":"shell","language":"shell","attribution":"Shell safety","explanation":"Quoting \"$@\" preserves each argument exactly, including ones containing spaces. Unquoted $@ splits them apart.","content":"for f in \"$@\"; do printf '%s\\n' \"${f%.*}\"; done"}, + {"category":"shell","language":"shell","attribution":"Shell safety","explanation":"A trap on EXIT cleans up whether the script succeeded, failed or was interrupted.","content":"tmp=$(mktemp -d) && trap 'rm -rf \"$tmp\"' EXIT"} +] -- cgit v1.2.3