json1-4 Exercise 1: Install jq Safely, Then Ten Filters ======================================================= Run in Git Bash on Windows 11: bash ex1.sh (the script installs into a temporary folder and deletes it at the end; nothing is added to PATH permanently) --- shop.json --- { "shop": "Corner Shop", "open": true, "owner": {"first": "Ada", "last": "Lovelace", "phone": null}, "products": [ {"sku": "A1", "name": "Tea", "price": 3.5, "stock": 40, "tags": ["drink", "hot"]}, {"sku": "B2", "name": "Coffee", "price": 4.25, "stock": 0, "tags": ["drink", "hot"]}, {"sku": "C3", "name": "Biscuits", "price": 1.99, "stock": 120, "tags": ["food"]}, {"sku": "D4", "name": "Milk", "price": 0.89, "stock": 15} ] } --- ex1.sh --- # Exercise 1: install jq into a scratch folder, verify it, run ten filters set -u dir=$(mktemp -d) ver=jq-1.8.2 base=https://github.com/jqlang/jq/releases/download/$ver curl -sL -o "$dir/jq.exe" "$base/jq-windows-amd64.exe" curl -sL -o "$dir/sha256sum.txt" "$base/sha256sum.txt" expected=$(grep 'jq-windows-amd64.exe' "$dir/sha256sum.txt" | cut -d' ' -f1) actual=$(certutil -hashfile "$(cygpath -w "$dir/jq.exe")" SHA256 | sed -n 2p | tr -d ' \r') echo "expected sha256: $expected" echo "actual sha256 : $actual" [ "$expected" = "$actual" ] && echo "checksum: MATCH" || echo "checksum: MISMATCH" # control: a corrupted copy must NOT match cp "$dir/jq.exe" "$dir/bad.exe"; printf 'x' >> "$dir/bad.exe" bad=$(certutil -hashfile "$(cygpath -w "$dir/bad.exe")" SHA256 | sed -n 2p | tr -d ' \r') [ "$expected" = "$bad" ] && echo "control: corrupted copy MATCHES (bad)" || echo "control: corrupted copy does not match (good)" export PATH="$dir:$PATH" jq --version echo run() { echo "\$ jq $*"; jq "$@" shop.json 2>&1; } run .shop run .owner.first run '.owner.phone' run '.products | length' run '.products[0].name' run '.products[].name' -c run '.products[] | select(.stock > 0) | .name' -r run '.products | map(.price) | add' run '.products[1:3] | map(.sku)' -c run '.products[] | select(.tags) | .name' -r rm -rf "$dir" Output: expected sha256: a6fc67fedaf9128a3309a1e2ebb8b986aeccf70122ee46d2cb4849e423f0c627 actual sha256 : a6fc67fedaf9128a3309a1e2ebb8b986aeccf70122ee46d2cb4849e423f0c627 checksum: MATCH control: corrupted copy does not match (good) jq-1.8.2 $ jq .shop "Corner Shop" $ jq .owner.first "Ada" $ jq .owner.phone null $ jq .products | length 4 $ jq .products[0].name "Tea" $ jq .products[].name -c "Tea" "Coffee" "Biscuits" "Milk" $ jq .products[] | select(.stock > 0) | .name -r Tea Biscuits Milk $ jq .products | map(.price) | add 10.63 $ jq .products[1:3] | map(.sku) -c ["B2","C3"] $ jq .products[] | select(.tags) | .name -r Tea Coffee Biscuits WHY THIS WORKS AS AN ANSWER --------------------------- The download is verified, not trusted: the expected hash comes from the project's own sha256sum.txt, the actual hash from certutil, and the script prints both so a mismatch would be visible. The control, a copy with one byte appended, shows the comparison can say "does not match". Installing into a temp folder keeps the change reversible. The ten filters cover one idea each: a key, a nested key, a null value (printed as null, not an error), length, an index, [] with a key (four results), select with -r (strings without quotes), add, a slice (items 1 and 2: the end index is excluded), and select(.tags) which keeps only products where tags is not null (Milk has no tags field, so it is left out). This used the jq 1.8.2 release binary for Windows amd64 only; I did not test winget, Homebrew or apt.