# Cross Platform Parsing

> Safe text and CLI output parsing patterns across Windows and Unix

- Skill: `chris2ao/cross-platform-parsing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add chris2ao/cross-platform-parsing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/chris2ao/cross-platform-parsing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: chris2ao (https://skillmd.com/u/chris2ao)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/chris2ao/cross-platform-parsing

---


# /cross-platform-parsing - Cross-Platform Text and CLI Parsing

Activate when writing code that parses CLI output, processes text files, or runs shell commands in a cross-platform (Windows + macOS/Linux) context. These patterns prevent silent data corruption from line-ending and shell-interpretation differences.

## Steps

### 1. CRLF-Safe Regex for Line Endings

Never use `/\n/` in regex patterns. Use `/\r?\n/` to handle both Windows CRLF and Unix LF:

```ts
// Wrong: fails silently on Windows files
const parts = text.split('\n')
const hasFrontmatter = /^---\n/.test(content)

// Correct
const parts = text.split(/\r?\n/)
const hasFrontmatter = /^---\r?\n/.test(content)
```

This applies to: frontmatter delimiters, line splitting, multiline regex patterns, and any file read from disk on Windows.

### 2. execFileSync Instead of execSync for Special Characters

On Windows, `execSync` routes through `cmd.exe` which interprets `%`, `^`, `!`, and `&` as shell metacharacters. Git format strings, path patterns, and many CLI flags contain these characters.

Use `execFileSync(command, argsArray)` to bypass shell interpretation entirely:

```ts
import { execFileSync } from 'child_process'

// Wrong: cmd.exe eats the % characters
execSync('git log --format="%H|%s|%an|%ai"')

// Correct: args passed directly without shell
execFileSync('git', ['log', '--format=%H|%s|%an|%ai'])
```

### 3. Never .trim() Positional CLI Output

CLI tools like `git status --porcelain` use leading whitespace as semantic codes. `.trim()` corrupts these:

```ts
// Wrong: strips the staging status code from " M filename.ts"
const line = rawLine.trim()

// Correct: strip only trailing newlines
const line = rawLine.replace(/[\r\n]+$/, '')

// For splitting into lines, filter empty rather than trimming each
const lines = output.replace(/[\r\n]+$/, '').split(/\r?\n/).filter(Boolean)
```

Formats with positional whitespace: `git status --porcelain`, `git diff --stat`, `column`-aligned output.

### 4. `set -a` Before Sourcing Secrets in MCP Wrapper Scripts

MCP wrapper scripts that launch `npx` or `uv run` against a sourced `secrets.env` must export every variable to child processes, not just set them in the current shell. Without `set -a`, the variables are assigned but not exported, and the MCP subprocess sees nothing.

```bash
#!/usr/bin/env bash
# Wrong: vars set in shell scope only; npx child sees nothing
source "$HOME/.claude/secrets/secrets.env"
exec npx -y @org/mcp-server

# Correct: set -a marks every subsequent assignment for export
set -a
source "$HOME/.claude/secrets/secrets.env"
set +a
exec npx -y @org/mcp-server
```

Rule: every MCP wrapper under `~/.claude/scripts/` that sources `secrets.env` must bracket the source with `set -a` / `set +a` (or equivalent `export VAR=...` assignments). If a fresh MCP says it cannot read its key, check the wrapper first.

### 5. Bash Tool Shell Is zsh — Word-Splitting Differs

The Claude Code Bash tool executes commands in **zsh**, not bash. This creates two silent failure modes:

**Mode A: Unquoted variable word-splitting.** In bash, `for f in $list` splits `$list` on IFS. In zsh, unquoted variables are NOT word-split by default, so the loop iterates once over the entire string as a single token. A payload copy loop that silently processed a single bogus path instead of many files is the classic symptom.

```bash
# Appears to work in bash, silently broken in zsh (Bash tool):
for f in $FILES; do cp "$f" "$DEST/"; done

# Safe in both: pass through bash explicitly or use an array
bash -c 'for f in '"$FILES"'; do cp "$f" "$DEST/"; done'
# Or, in scripts with a bash shebang, arrays work correctly:
for f in "${FILES[@]}"; do cp "$f" "$DEST/"; done
```

**Mode B: Interactive-shell errors are red herrings for bash-shebang scripts.** `source`-ing a bash library in the Bash tool's zsh shell may produce errors (e.g., zsh-style `read -A` failures) even though the script runs correctly under its `#!/usr/bin/env bash` shebang. bats test suites also run under real bash. Before diagnosing a "broken" script from interactive output, re-run it under the target shell:

```bash
bash -c 'source ./lib.sh && test_function'
# Or run the script directly so the shebang takes effect:
./scripts/my-script.sh
```

Evidence: CJClaudin_Setup session 3f16f8dd (2026-05-24). "The copy loop didn't word-split (the tool's shell is zsh, which doesn't split unquoted vars like bash). / My `source` ran under the tool's interactive shell (zsh-style `read -a` failure), not bash. The bats suite (10/10) runs under real bash."

### 6. Bootstrap and Installer Step Loops Require set -ue

When a bootstrap or installer script iterates over numbered step scripts, use `set -ue` (not just `set -uo pipefail`). Without `-e`, a failing step does not halt the loop, so later steps run against a blank or broken environment.

```bash
#!/usr/bin/env bash
# Wrong: a failure in step 02 lets steps 03-08 run against a broken environment
set -uo pipefail

for step in steps/*.sh; do
  bash "$step"
done

# Correct: exit immediately on any step failure
set -ueo pipefail

for step in steps/*.sh; do
  bash "$step"
done
```

Also test the partial-run path (`--only <step>`) to confirm it exits nonzero when the targeted step fails. Both the full run and the partial run must fail fast.

Evidence: CJClaudin_Setup `bootstrap.sh` used `set -uo pipefail` without `-e`. A failure in step 02 (repo clone) would have let steps 03-08 configure MCP servers and verify against an empty checkout. Caught in Phase 5 code review.

## Source Instincts

- `use-crlf-safe-regex`: "when writing regex to match line endings in cross-platform code"
- `use-execfilesync-on-windows`: "when using execSync with format strings containing %, ^, !, or & on Windows"
- `no-trim-on-structured-cli-output`: "when parsing structured CLI output where whitespace has semantic meaning"
- `set-a-before-source-secrets`: "when writing an MCP wrapper script that sources secrets.env before launching npx or uv run"
- `verify-under-target-shell`: "when a shell script misbehaves in the harness shell or an interactive shell"
- `bootstrap-step-loop-set-e`: "when writing bootstrap/installer scripts that run numbered steps in a loop"

