Skip to content

feedback(hooks-plugin): bash-antipatterns awk check false-positives on read-only awk before a downstream redirect #2131

Description

@laurigates

Skill / hook

hooks-plugin/hooks/bash-antipatterns.sh (v2.7.0), the "awk used for file modifications" guard (lines ~301-303).

Category

Bug (false positive)

Description

The guard blocks awk used read-only in a pipeline whenever a downstream command in the same pipeline redirects to a variable path. The intent is to catch awk '...' > file (awk rewriting a file); it instead also fires on awk '...' | sort -u > "$file", where awk only extracts fields and sort does the writing.

Root cause — condition 1's .* is greedy and spans the pipe:

if echo "$COMMAND" | grep -Eq "awk\s+.*>\s*['\"]?[^|]+" && \
   echo "$COMMAND" | grep -Eq "(>|>>)\s*['\"]?\\\$"; then

awk\s+.*> matches awk ... | sort -u > because .* crosses the |. The [^|]+ only constrains the char after >, not the span between awk and >.

Evidence

Hit this session on:

chezmoi managed 2>/dev/null | awk -F/ '{print $1}' | sort -u > "$SP/chezmoi-toplevel.txt"

-> blocked with "Use the Edit tool instead of 'awk' for file modifications" (had to rewrite the read-only awk -F/ '{print $1}' as cut -d/ -f1). Two further live demonstrations in the same session: a test-harness command and even the gh issue create for this very report were both blocked, because each merely mentions awk ... > "$..." inside a quoted string literal.

Reproduction against the two conditions:

Command Expected Actual
... | awk -F/ '{print $1}' | sort -u > "$SP/f.txt" (awk read-only) pass BLOCKED
awk '{print $1}' data.txt > "$OUT/f.txt" (awk writes) BLOCKED BLOCKED
... | awk -F/ '{print $1}' | sort -u (no redirect) pass pass

Suggested Action

Constrain condition 1 so a pipe between awk and the redirect prevents the match:

# was:  awk\s+.*>\s*['\"]?[^|]+
# use:  awk\s+[^|]*>\s*['\"]?[^|]+
if echo "$COMMAND" | grep -Eq "awk\s+[^|]*>\s*['\"]?[^|]+" && \
   echo "$COMMAND" | grep -Eq "(>|>>)\s*['\"]?\\\$"; then

awk\s+[^|]*> matches awk '...' > file (true positive) but not awk ... | sort > file (the redirect belongs to a downstream command). Re-running the table above with the fix: row 1 -> pass, row 2 -> BLOCKED, row 3 -> pass.

Note: the same greedy-.*-across-pipes shape may affect sibling checks in the hook worth a quick audit (e.g. the sed/cut chain and commit-file guards).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsession-feedbackFeedback from session analysis

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions