Safe Workflows

Patterns for operations that need human confirmation before acting. Three layers of increasing robustness: glob loop, find -exec, and null-delimited xargs.

Safe Workflows

Patterns for operations that need human confirmation before acting. The principle: show what you found, prove it’s correct, then ask permission.

Interactive Delete with Validation

Layer 1 — glob + confirm loop (typed by hand at CLI)
for f in tobe*.adoc; do
    echo "Found: $f ($(wc -c < "$f") bytes)"
    head -1 "$f"
    read "reply?Delete $f? [y/N] "
    [[ "$reply" == "y" ]] && rm "$f" && echo "Deleted." || echo "Kept."
done
# Output (real session):
# Found: tobeorganized-siem.adoc (21246 bytes)
# Now I have the full picture. The correct standard for your system is:
# Delete tobeorganized-siem.adoc? [y/N] y
# Deleted.
# Found: tobe-reviewed-agnostic-sys-docs-exploration.adoc (2757 bytes)
# = The Agnostic Engineer's Discovery Protocol
# Delete tobe-reviewed-agnostic-sys-docs-exploration.adoc? [y/N] y
# Deleted.

What each line does:

  • for f in tobe*.adoc — shell expands the glob, iterates each match

  • wc -c < "$f" — byte count (input redirection avoids filename in output)

  • head -1 "$f" — show first line so you can identify the file’s content

  • read "reply?Delete $f? [y/N] " — zsh prompt syntax (? separates variable from prompt text)

  • [[ "$reply" == "y" ]] — only exact y proceeds; anything else (Enter, n, typo) keeps the file

  • && rm "$f" && echo "Deleted." — short-circuit: rm only runs if reply is y, echo only if rm succeeds

  • || echo "Kept." — fires if any part of the && chain fails (including reply != y)

Layer 2 — find + exec for arbitrary paths (script-safe)
find . -maxdepth 1 -name 'tobe*.adoc' -type f -exec sh -c '
    for f; do
        echo "--- $f ---"
        head -1 "$f"
        wc -l < "$f"
        printf "Delete? [y/N] "
        read reply
        [ "$reply" = "y" ] && rm "$f" && echo "Deleted." || echo "Kept."
    done
' _ {} +
# find handles filenames with spaces, special characters
# sh -c '...' _ {} + passes all found files as arguments to the script
# _ is a placeholder for $0 (the script name)
Layer 3 — null-delimited for maximum safety
find . -maxdepth 1 -name 'tobe*.adoc' -print0 | xargs -0 -n1 sh -c '
    printf "%s (%s lines)\nFirst line: %s\nDelete? [y/N] " "$1" "$(wc -l < "$1")" "$(head -1 "$1")"
    read reply
    [ "$reply" = "y" ] && rm "$1" && echo "Deleted." || echo "Kept."
' _
# -print0 + xargs -0 = null-delimited = safe for ANY filename
# -n1 = one file per invocation so read works correctly

When to Use Which

Layer Use when Limitation

Glob loop

Quick one-off at the CLI, filenames are predictable

Glob only matches current directory pattern

find -exec

Need path traversal, type filters, or this goes in a script

Slightly more verbose

find + xargs -0

Filenames may contain spaces, quotes, or newlines

Most verbose but bulletproof

The Validation Pattern

Every safe workflow follows the same structure:

1. FIND    — identify targets (glob, find, grep -rl)
2. SHOW    — display enough context to confirm identity (head, wc, file)
3. ASK     — prompt for confirmation (read, -p, -i)
4. ACT     — execute only on explicit yes (&&)
5. REPORT  — confirm what happened (echo, diff)

This is STD-008 (verify-before/change/verify-after) applied to interactive workflows.

Capture to File Before Executing

The progression: type at CLI → capture to /tmp/ → inspect → execute → promote.

Write script via heredoc, inspect, then run
# 1. WRITE — heredoc to temp file
#    'EOF' (single-quoted) prevents $variable expansion at write time
tee /tmp/safe-delete.sh << 'EOF'
#!/usr/bin/env bash
for f in tobe*.adoc; do
    echo "Found: $f ($(wc -c < "$f") bytes)"
    head -1 "$f"
    read -p "Delete $f? [y/N] " reply
    [[ "$reply" == "y" ]] && rm "$f" && echo "Deleted." || echo "Kept."
done
EOF

# 2. INSPECT — read it back, verify before executing
cat /tmp/safe-delete.sh

# 3. EXECUTE — only after visual confirmation
bash /tmp/safe-delete.sh

# 4. PROMOTE — if it worked, save permanently
# cp /tmp/safe-delete.sh ~/bin/safe-delete.sh && chmod +x ~/bin/safe-delete.sh
Why single-quoted 'EOF'?
# WRONG — unquoted EOF expands variables at WRITE time
tee /tmp/broken.sh << EOF
echo "User is $USER"       # writes: echo "User is evan" (baked in)
EOF

# CORRECT — single-quoted 'EOF' preserves variables for RUNTIME
tee /tmp/correct.sh << 'EOF'
echo "User is $USER"       # writes literally: echo "User is $USER"
EOF
Capture build output for review
make 2>&1 | tee /tmp/build-output.log | grep -E 'WARN|ERROR'
# tee splits the stream: full output to file, filtered output to terminal
# Review later: cat /tmp/build-output.log
Capture a pipeline for reuse
tee /tmp/xref-fix.sh << 'EOF'
#!/usr/bin/env bash
# Fix broken xrefs after directory rename
grep -rlP 'xref:codex/cli/' --include='*.adoc' docs/modules/ROOT/ | \
    xargs sed -i \
        -e 's|xref:codex/cli/grep\.adoc|xref:codex/grep/index.adoc|g' \
        -e 's|xref:codex/cli/sed\.adoc|xref:codex/sed/index.adoc|g'
# Verify
grep -rnP 'xref:codex/cli/' --include='*.adoc' docs/modules/ROOT/
EOF
cat /tmp/xref-fix.sh          # inspect
bash /tmp/xref-fix.sh         # execute after review

Common Gotchas

Colon vs semicolon in for loops
# WRONG — colon is not a statement terminator
for f in *.adoc: do    # zsh: no matches found: *.adoc:

# CORRECT — semicolon separates the list from do
for f in *.adoc; do
Quoted glob prevents expansion
# WRONG — quotes prevent glob expansion
ls "*.adoc"             # looks for literal file named "*.adoc"

# CORRECT — unquoted glob expands
ls *.adoc               # shell expands to matching files
Regex is not glob
# WRONG — ^ and | are regex operators, not glob
ls ^tobe|*.adoc         # | is a pipe, sends ls output to command "*.adoc"

# CORRECT — glob syntax for filtering
ls tobe*.adoc           # glob: tobe followed by anything, ending in .adoc

# If you need regex-level filtering on filenames:
ls *.adoc | grep '^tobe'
find . -maxdepth 1 -regex './tobe.*\.adoc'
zsh read syntax differs from bash
# zsh — prompt after ? inside the variable name
read "reply?Continue? [y/N] "

# bash — use -p flag for prompt
read -p "Continue? [y/N] " reply

See Also

  • Loops — for/while/until fundamentals

  • Tests — conditional expressions

  • Glob Patterns — wildcard expansion

  • find — filesystem search predicates

  • xargs — null-delimited pipeline safety