Skip to content

Commands fail on zsh: $(...) command substitution not portable #973

@peedrr

Description

@peedrr

Given the ubiquity of zsh and a repo with > 5k stars, I find it kind of hard to believe that I'm the first to notice this, but commands in .claude/commands/pm/epic-sync.md (and likely other files) fail when the user's default shell is zsh instead of bash.

The Problem

The "Bash" tool in Claude Code executes commands through the user's default shell, not necessarily bash. Commands using $(...) command substitution fail with parse errors on zsh.

Related Claude Code issues:

Issue #7507 suggests this might actually be a regression in Claude Code - no response from Anthropic for a fix as yet - and I realise that most likely Claude itself created the Bash commands used within this repo (I've encountered this before and also... Claude doesn't need to use temporary env vars as the output is shown!) however, until (if/when) Anthropic fix this, the backtick substitution solution shown below should be pretty universal.

Environment

  • Default shell: zsh 5.9
  • Bash available: GNU bash 5.3.3
  • OS: NixOS/Linux

Test Results

Test 1: zsh with $(...) - FAILS

remote_url=$(git remote get-url origin 2>/dev/null || echo "") && REPO=$(echo "$remote_url" | sed 's|.*github.com[:/]||' | sed 's|\.git$||') && echo "$REPO"

Result: (eval):1: parse error near ')'

Test 2: bash -c wrapper - WORKS

bash -c 'remote_url=$(git remote get-url origin 2>/dev/null || echo "") && REPO=$(echo "$remote_url" | sed "s|.*github.com[:/]||" | sed "s|\.git$||") && echo "$REPO"'

Result: user/my-repo

Test 3: zsh with backticks - WORKS

remote_url=`git remote get-url origin 2>/dev/null || echo ""` && REPO=`echo "$remote_url" | sed 's|.*github.com[:/]||' | sed 's|\.git$||'` && echo "$REPO"

Result: user/my-repo

Proposed Solutions

  1. Replace $(...) with backticks - More portable across shells
  2. Wrap commands in bash -c '...' - Forces bash execution
  3. Restructure to avoid command substitution - Have Claude hold state between tool calls

Option 1 (backticks) seems most straightforward for maintaining compatibility.

Affected Files

At minimum:

  • .claude/commands/pm/epic-sync.md (lines 60-64, and elsewhere)

Likely others throughout the repo that use $(...) syntax.

Happy to submit a PR if you'd like! Just let me know which approach you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions