- Posted on
- • Artificial Intelligence
Bash Automation for Large Data Sets
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Bash Automation for Large Data Sets: From Hours to Minutes
If you’ve ever watched a progress bar crawl while a GUI tool tries to ingest a multi‑gigabyte file, you know the pain. Large data sets strain memory, exhaust CPUs, and expose every inefficiency in your workflow. The good news: with Bash and a handful of battle‑tested CLI tools, you can stream, filter, aggregate, and ship terabytes with surprising speed and reliability—often without writing a single line of Python.
This article shows why Bash is still a top-tier choice for big data plumbing and gives you actionable patterns, commands, and installation steps to go from “stuck” to “shipped.”
Why Bash for big data?
Stream-first, low memory: Unix tools process data line-by-line, so you don’t need to load everything into RAM.
Composable: Pipes let you stitch specialized tools into robust, fast pipelines.
Reproducible and portable: Shell scripts run the same on dev boxes, servers, and containers.
Parallel by default: It’s easy to fan out work across CPU cores and merge results.
Transparent performance: You can see and measure each stage with standard tools.
Install the toolkit
The examples below use common packages. Install them first. If a package isn’t found, search it by name (e.g., apt search, dnf search, or zypper search) or enable the appropriate repo.
Tools we’ll use:
GNU parallel (parallelization)
ripgrep (rg) for fast text filtering
jq (JSON processing)
Miller (mlr) for CSV/TSV processing
csvkit (CSV utilities; optional if you prefer Miller)
pigz (parallel gzip)
pv (progress/throughput viewer)
datamash (quick aggregates)
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y parallel ripgrep jq miller csvkit pigz pv datamash
Fedora (dnf):
sudo dnf install -y parallel ripgrep jq miller python3-csvkit pigz pv datamash
RHEL/CentOS (dnf + EPEL):
sudo dnf install -y epel-release
sudo dnf install -y parallel ripgrep jq miller python3-csvkit pigz pv datamash
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y gnu_parallel ripgrep jq miller python3-csvkit pigz pv datamash
Note: On some distros gnu_parallel is named parallel. Use zypper search parallel if unsure.
1) Stream, don’t load: build pipelines that never keep more than a buffer in RAM
Load-it-all-at-once is the enemy of scale. Prefer streams and simple, fast filters.
- Work on compressed data directly when possible:
# Stream .gz logs, search for ERROR fast, count occurrences
pigz -dc logs/*.gz | rg -N "ERROR" | wc -l
- Use locale and memory knobs on sort for speed and scale:
# Faster byte-wise comparison, bounded memory, spill to fast scratch if needed
LC_ALL=C sort -S 50% -T /mnt/fasttmp bigfile.tsv > sorted.tsv
- Peek before you leap: sample to validate logic on huge files:
# Random sample 10k lines to dry-run a pipeline
shuf -n 10000 big.csv | mlr --csv stats1 -a count,mean -f bytes
- Show progress/throughput:
pv big.json.gz | pigz -dc | jq -r '.user_id' | wc -l
Why it works: Most Unix tools operate in O(n) streaming passes; they’re fast, predictable, and avoid memory blowups.
2) Scale out locally with GNU parallel
Parallelizing per-file or per-chunk work can cut wall-clock time dramatically.
- Fan out per-file and merge:
# Extract 5xx API hits per file in parallel, merge and sum
find logs -name '*.json.gz' -print0 |
parallel -0 -j8 'pigz -dc {} | jq -r "select(.status>=500) | .app"' |
sort | uniq -c | sort -nr > top_apps.txt
- Chunk a huge single file by blocks and process in parallel:
# Split into ~100MB chunks on line boundaries, process with --pipe
pigz -dc big.json.gz |
parallel --pipe --block 100M -j8 'jq -r "select(.country==\"US\") | .user_id"' |
sort -u > us_users.txt
- Be safe with filenames (NUL-delimited):
find data -type f -name '*.csv' -print0 | parallel -0 -j$(nproc) 'mlr --csv cut -f id,bytes {} > {.}.min.csv'
Tip: Use --dry-run with parallel to inspect what will execute.
3) Tame JSON and CSV at scale (jq + Miller)
Semi-structured text dominates large-scale telemetry. Use tools purpose-built for line-oriented data.
- JSON filtering and aggregation:
# Top 10 endpoints by 5xx responses from gzipped NDJSON
pigz -dc api.ndjson.gz |
jq -r 'select(.status>=500) | .endpoint' |
sort | uniq -c | sort -nr | head -n 10
- CSV per-group stats with Miller:
# Sum and average bytes by user_id
pigz -dc traffic.csv.gz |
mlr --csv stats1 -a count,sum,mean -f bytes -g user_id |
mlr --csv sort -nr mean_bytes > bytes_by_user.csv
- Joining large CSVs on a key without loading everything into RAM:
# Left-join metadata onto events by user_id
pigz -dc events.csv.gz |
mlr --csv join --ul -j user_id -f metadata.csv then cut -o -f user_id,event,plan > enriched.csv
- Quick aggregates with datamash:
# datamash expects delimited columns; here, comma-separated CSV
datamash -t, -g country sum 3 mean 3 < small.csv
Why it works: jq and Miller are streaming-friendly and optimized for text-based records, avoiding full in-memory parses.
4) Make jobs reliable: idempotent, restartable Bash
Long jobs must survive hiccups. Add guardrails: strict modes, traps, temp dirs, checkpoints, and logs.
- Script skeleton:
#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit
log() { printf '%s %s\n' "$(date +%FT%T)" "$*" >&2; }
workdir="$(mktemp -d)"
cleanup() { rm -rf "$workdir"; }
trap cleanup EXIT
INPUT="${1:?usage: $0 INPUT_DIR}"
OUT="report.csv"
# Checkpoints
STEP1_DONE=".step1.done"
STEP2_DONE=".step2.done"
if [[ ! -e $STEP1_DONE ]]; then
log "Step 1: extract 5xx hits"
find "$INPUT" -name '*.json.gz' -print0 |
parallel -0 -j"$(nproc)" 'pigz -dc {} | jq -r "select(.status>=500) | .app"' \
| sort | uniq -c | sort -nr > "$workdir/apps.txt"
touch "$STEP1_DONE"
fi
if [[ ! -e $STEP2_DONE ]]; then
log "Step 2: summarize to CSV"
awk 'BEGIN{OFS=","} {print $2,$1}' "$workdir/apps.txt" > "$OUT"
touch "$STEP2_DONE"
fi
log "Done. Output: $OUT"
- Tips:
- Write deterministic outputs so reruns are safe (idempotent).
- Use
.donefiles per stage to resume work. - Log with timestamps; keep stderr for logs and stdout for data.
5) Measure, then optimize
Guessing wastes time. Instrument your pipelines.
- Baseline commands:
/usr/bin/time -v bash -c 'pigz -dc big.ndjson.gz | jq -c "select(.ok==true)" | wc -l'
- Compare alternatives:
# ripgrep vs grep, locale vs default, parallel vs serial
pigz -dc logs.gz | rg -N 'pattern' > /dev/null
pigz -dc logs.gz | LC_ALL=C grep -F 'pattern' > /dev/null
- Profile bottlenecks:
- Is it CPU-bound? Try
-j$(nproc)orpigzinstead ofgzip. - Is it disk-bound? Stream, avoid temp explosions, use
-Son sort and a fast-Ttmpdir. - Is it network-bound? Compress on the wire:
... | pigz -c | ssh host 'pigz -dc | ...'.
- Is it CPU-bound? Try
Real-world mini playbook: daily TB of gzipped API logs
Goal: Count 5xx per endpoint, top 100, daily.
# 1) Fan out per file, emit endpoints for 5xx
find /data/logs/2026-07-06 -name '*.json.gz' -print0 |
parallel -0 -j12 'pigz -dc {} | jq -r "select(.status>=500) | .endpoint"' |
sort | uniq -c | sort -nr | head -n 100 > top_5xx_endpoints.txt
# 2) Persist CSV
awk 'BEGIN{OFS=","} {print $2,$1}' top_5xx_endpoints.txt > top_5xx_endpoints.csv
Scales well, streams everything, and keeps memory flat.
Common pitfalls (and fixes)
“It’s slow.” Measure first (
/usr/bin/time -v,pv), then parallelize and tunesort.“Out of space in /tmp.” Use
sort -T /mnt/fasttmpand ensure enough scratch.“Weird collation or sort order.” Set
LC_ALL=Cfor byte-wise speed and reproducibility.“Spaces in filenames broke my job.” Use NUL delimiters:
-print0withfindand-0withparallel/xargs.
Conclusion and next step
Bash remains a powerhouse for large data automation because it streams by default, composes cleanly, and scales across cores with minimal code. Your next step:
1) Install the toolkit (parallel, ripgrep, jq, miller, csvkit, pigz, pv, datamash).
2) Convert one slow, memory-heavy task into a streaming pipeline.
3) Add parallel and checkpoints for reliability and speed.
4) Measure, iterate, and document as a script.
Pro tip: keep a “pipeline cookbook” repo of proven one-liners and scripts. Future you—and your compute bill—will thank you.