Chapter 08Lesson 05~95 minutes

Composing Text Pipelines without Fragile Parsing

Pipelines are robust only when each stage has a clear contract. Fragility appears when human-formatted output is treated as an API, whitespace is assumed to be structure, or structured data is flattened before a real parser sees it.

BeginnerText processingHands-on lab

Learning objectives

By the end of this lesson

  • Define record boundaries before composing tools.
  • Separate human-readable output from machine data.
  • Use pipefail and stderr contracts.
  • Handle arbitrary filenames safely.
  • Preserve structured formats until a proper parser extracts simple data.

1. Define record boundaries before building the pipeline

FormatBoundaryWhen appropriate
Line textNewline separates recordsOnly safe if records cannot contain newlines
NUL-delimited pathsNUL separates filenamesSafe for arbitrary Unix filenames
Simple TSV/delimited textKnown delimiter separates fieldsRequires an explicit escaping/content contract
JSONStructured syntaxUse a JSON-aware parser

2. Human-readable output is not automatically an API

Headers, alignment, abbreviations, color, locale, and version-specific formatting can all make display output brittle to parse.

Ask for machine-readable output first

Check whether the source command already provides JSON, TSV, NUL-delimited, no-header, or explicit-field modes before parsing display text.

3. Prefer one coherent transformation over needless conversions

# Several stages:
cut -d: -f1,2 services.txt |
grep ':prod$' |
cut -d: -f1

# One field-aware stage:
awk -F: '$2 == "prod" { print $1 }' services.txt

4. Make upstream failure visible

set -o pipefail

if generate_inventory |
   normalize_records |
   validate_records > inventory.txt; then
  printf 'pipeline succeeded\n'
else
  status=$?
  printf 'pipeline failed: %d\n' "$status" >&2
  exit "$status"
fi

5. Keep diagnostics out of the data stream

producer   2> >(tee producer.log >&2) |
transformer |
consumer

stdout can remain a clean record stream while stderr remains useful to humans and CI logs.

6. Control locale when deterministic ordering matters

LC_ALL=C sort input.txt

Locale affects collation and some character classes. Set it explicitly when bytewise ordering is part of the automation contract.

7. Use NUL for arbitrary filenames

find artifacts -type f -print0 |
while IFS= read -r -d '' file; do
  sha256sum -- "$file"
done

8. Keep structured data structured

curl --fail --silent --show-error "$url" |
jq -r '.services[] | select(.enabled) | [.name, .replicas] | @tsv' |
while IFS=$'\t' read -r name replicas; do
  printf 'service=%s replicas=%s\n' "$name" "$replicas"
done
Do not parse JSON with grep/sed

JSON whitespace, escaping, ordering, and nesting require a JSON-aware parser.

9. Temporary artifacts are sometimes more reliable than streaming

Use a temporary file when you need to validate a complete intermediate result, preserve evidence after failure, feed multiple consumers, or retry only a later stage.

tmp=$(mktemp)

if generate_inventory >"$tmp" &&
   validate_inventory "$tmp"; then
  process_inventory "$tmp"
  status=$?
else
  status=$?
fi

rm -f -- "$tmp"
exit "$status"

10. Let Bash orchestrate specialist parsers

Separation of responsibilities
flowchart LR
  B["Bash orchestration"] --> P["jq / awk / parser"]
  P --> R["simple records"]
  R --> B
  B --> C["CLI actions"]

11. Hands-on lab: robust production inventory pipeline

mkdir -p "$HOME/devops-academy/bash/chapter08/lesson05"
cd "$HOME/devops-academy/bash/chapter08/lesson05"

cat > services.txt <<'EOF'
api:prod:3
worker:prod:2
cache:staging:1
metrics:prod:1
EOF

set -o pipefail

if report=$(
  awk -F: '$2 == "prod" { print $1 ":" $3 }' services.txt |
  LC_ALL=C sort
); then
  printf '%s\n' '--- production inventory ---'
  printf '%s\n' "$report"
else
  status=$?
  printf 'inventory pipeline failed: %d\n' "$status" >&2
  exit "$status"
fi

printf 'prod_service_count=%s\n' \
  "$(printf '%s\n' "$report" | wc -l)"

Verification checklist

12. Knowledge check

Question 1. Why is human-formatted output risky to parse?

Question 2. What delimiter is safe for arbitrary Unix filenames?

Question 3. Why use pipefail?

Question 4. How should JSON be handled?

13. Summary

Robust pipelines are built around explicit record contracts. Prefer machine-readable producer modes, keep stderr separate, use NUL for filenames, control locale when needed, expose pipeline failure, and let structured parsers own structured formats.

14. Further reading

  • GNU Coreutils and GNU findutils manuals.
  • GNU Bash Reference Manual — Pipelines, Redirections, and pipefail.
  • jq Manual.
  • POSIX utility specifications for grep, sed, awk, sort, and related tools.
Next lesson

Safe Path Handling and Filename Edge Cases

Chapter 9 will apply these data-boundary principles to filesystem automation, safe paths, find/xargs, temporary files, permissions, and atomic updates.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.