diff options
| author | srdusr <[email protected]> | 2026-01-14 22:23:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-01-14 22:23:00 +0200 |
| commit | 4933f669f8338ca5e4c64cf0294818c5d15ed122 (patch) | |
| tree | 979ce6ffed0a4a59f65f47252ce9824a77be760e /data/packs/shell.json | |
| parent | 695bf0df2ec48e67d716dad25711cdc2e96bf7f4 (diff) | |
| download | typerpunk-4933f669f8338ca5e4c64cf0294818c5d15ed122.tar.gz typerpunk-4933f669f8338ca5e4c64cf0294818c5d15ed122.zip | |
Make every passage long enough to be worth timing, and credit all of them
The dataset's median passage was 69 characters, about twelve words, which is
over in twelve seconds at an ordinary speed. Some packs were worse: shell had
a median of 46 and a shortest entry of 17. That is the reason packs felt
small. It was not the number of entries, which was 15 to 37 per pack, but the
length of each one.
Multiplayer had already hit this and worked around it: load_race_texts
filters to 120 characters or more. That filter left only 61 of 350 passages
eligible, 38 of them from two packs, so races repeated constantly and four
packs could never come up at all.
Both halves are fixed.
Content: 104 longer passages added across every pack. The command packs get
whole pipelines rather than single flags, which is how the tools are actually
used and what the explanations were always for. Prose packs get passages that
run 150 to 320 characters.
Selection: a floor of 120 characters. Prose packs draw from their long
entries where a pack holds at least five, so no pack is reduced to the same
few passages. The command packs chain consecutive entries into one drill,
which is the natural shape for them, and the explanations are collected so
each line is still described.
Measured over 400 draws per pack, every category now runs a median of 146 to
218 characters with a shortest draw of 120. The race pool went from 61 of 350
to 130 of 454.
Attribution: 24 passages had none and displayed nothing at all under the
text. They now say Unknown, which is the honest answer for a fact written for
the pack, and the pangram is credited as one. The results and typing screens
both fall back to Unknown rather than rendering an empty line, and the pack
name is shown beside the source.
Multiplayer results, three fixes:
- PB never appeared. A race has no mode of its own, so modeKey was undefined,
so recordResult never ran. Races share one key, because the passage is
whatever the server dealt and a per-passage best would never be beaten.
- Play Again started a solo test. A race carries standings and a solo run does
not, so the results screen can tell them apart and now queues for another
race. The end screen's own cleanup leaves the old room first.
- CONSISTENCY was the longest label on the screen and made the accuracy
column wider than the WPM column opposite it. It reads CON.
RaceText carries the pack name so the results can show it for a race the same
way single player does.
24 Rust tests and 3 browser tests pass.
Diffstat (limited to 'data/packs/shell.json')
| -rw-r--r-- | data/packs/shell.json | 276 |
1 files changed, 252 insertions, 24 deletions
diff --git a/data/packs/shell.json b/data/packs/shell.json index 3545ef9..b7f8312 100644 --- a/data/packs/shell.json +++ b/data/packs/shell.json @@ -1,26 +1,254 @@ [ - {"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"} + { + "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" + }, + { + "category": "shell", + "content": "awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1, $6}' /etc/passwd", + "attribution": "awk", + "explanation": "Two conditions joined with &&. $3 >= 1000 keeps ordinary user accounts, and !~ excludes any shell matching nologin or false, so what is left is the humans who can log in.", + "language": "shell" + }, + { + "category": "shell", + "content": "awk '{sum += $1; n++} END {if (n) printf \"%.2f\\n\", sum / n}' numbers.txt", + "attribution": "awk", + "explanation": "Variables in awk need no declaration and start at zero. END runs once after the last line, so this accumulates while reading and prints the mean at the finish.", + "language": "shell" + }, + { + "category": "shell", + "content": "awk 'NR == FNR {seen[$1]; next} !($1 in seen)' first.txt second.txt", + "attribution": "awk", + "explanation": "NR is the overall line number and FNR restarts per file, so NR == FNR is true only while reading the first file. This prints lines of the second file whose first field never appeared in the first.", + "language": "shell" + }, + { + "category": "shell", + "content": "sort access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20", + "attribution": "shell", + "explanation": "uniq -c counts runs of identical adjacent lines, which is why the sort before it is required. sort -rn then orders those counts highest first, giving the twenty busiest addresses.", + "language": "shell" + }, + { + "category": "shell", + "content": "find . -type f -name '*.log' -mtime +30 -print0 | xargs -0 rm -v", + "attribution": "find", + "explanation": "-print0 separates paths with a null byte and -0 tells xargs to expect that, which is the only safe way to pass file names containing spaces or newlines.", + "language": "shell" + }, + { + "category": "shell", + "content": "grep -rn --include='*.rs' -e 'unwrap()' -e 'expect(' src/ | wc -l", + "attribution": "grep", + "explanation": "-r walks the tree, --include limits it to one file type, and repeated -e adds alternative patterns. Counting the result gives a rough measure of how much error handling is deferred.", + "language": "shell" + }, + { + "category": "shell", + "content": "sed -i.bak -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\\3\\/\\2\\/\\1/g' dates.csv", + "attribution": "sed", + "explanation": "-i.bak edits in place and keeps the original alongside. The parenthesised groups are recalled as \\1, \\2 and \\3, which is how the date order is rearranged.", + "language": "shell" + }, + { + "category": "shell", + "content": "tar czf - /var/www | ssh backup@host 'cat > site-$(date +%F).tar.gz'", + "attribution": "tar", + "explanation": "A dash as the file name makes tar write to standard output. The archive is never stored locally: it streams straight down the ssh connection into a file on the far side.", + "language": "shell" + }, + { + "category": "shell", + "content": "ps -eo pid,ppid,rss,comm --sort=-rss | head -15", + "attribution": "ps", + "explanation": "-eo picks exactly which columns to print. rss is resident memory in kilobytes, and the minus sign in --sort reverses the order, so the heaviest processes come first.", + "language": "shell" + }, + { + "category": "shell", + "content": "diff <(sort a.txt) <(sort b.txt) | grep '^[<>]'", + "attribution": "shell", + "explanation": "Process substitution gives each command a file name of its own, so diff can compare two pipelines without either being written to disk first.", + "language": "shell" + }, + { + "category": "shell", + "content": "curl -sS -w '%{http_code} %{time_total}s\\n' -o /dev/null https://example.com", + "attribution": "curl", + "explanation": "-o /dev/null throws the body away while -w prints chosen variables, which turns curl into a quick check of status and latency alone.", + "language": "shell" + }, + { + "category": "shell", + "content": "journalctl -u nginx --since '1 hour ago' -p err --no-pager | tail -50", + "attribution": "journalctl", + "explanation": "-p err keeps only entries at error priority or worse. --no-pager matters in a script, where an interactive pager would otherwise wait for a keypress that never comes.", + "language": "shell" + } ] |