◉CLAUDE LABJP
●2.1.295 — Hooks gain onFailure: "block", so a failed or timed-out hook no longer lets the action through●SONNET 4.5 — Retires on the Claude API on 11/30, 52 days left. Migrate to Sonnet 5.5●HAIKU 5.5 — Code written for Haiku 4.5 hits a 400 error on budget_tokens●Q&A — People are asking how to use one connector with two different accounts●API CACHE — Sonnet 5.5 cache reads drop to $0.10/Mtok (Oct 7)●NEW — Before halving always-loaded instruction files: sort what to keep, move, or delete●2.1.295 — Hooks gain onFailure: "block", so a failed or timed-out hook no longer lets the action through●SONNET 4.5 — Retires on the Claude API on 11/30, 52 days left. Migrate to Sonnet 5.5●HAIKU 5.5 — Code written for Haiku 4.5 hits a 400 error on budget_tokens●Q&A — People are asking how to use one connector with two different accounts●API CACHE — Sonnet 5.5 cache reads drop to $0.10/Mtok (Oct 7)●NEW — Before halving always-loaded instruction files: sort what to keep, move, or delete
Articles/Claude Code
⟐ Claude Code/2026-08-25Intermediate

One Space in a Folder Name Turned 80 Checks Into Zero

An inspection loop reported 80 files checked and 0 readable. The files were fine. Here is how word splitting turns path fragments into real directories, measured side by side, plus the count assertion I now put in front of every delete-heavy batch.

Claude Code259bash5automation109indie development25operations30

✦ Premium Article

I wanted a quick pass over the documents scattered through a working folder, so I wrote a short loop: match by extension, read the first byte, count what opens. Roughly 80 files.

It printed this.

checked=80 readable=0 unreadable=80

Eighty files checked, none readable. My first suspicion was sync. The folder lives under cloud storage, so I assumed the contents had not been materialized yet.

But opening any one of them with cat worked fine. Nothing was missing.

The files were never the problem. The folder name contained a single space. That was the whole story.

The 80 files had become 160 iterations

The loop looked like this — probably the most commonly written shape in personal inspection scripts.

for f in $(find "$ROOT/_documents" -type f -name "*.md"); do
  if head -c 1 "$f" >/dev/null 2>&1; then ok=$((ok+1)); else ng=$((ng+1)); fi
done

"$ROOT/_documents" is quoted. The loop variable "$f" is quoted. It still breaks.

What breaks is outside the $( ). The result of a command substitution sits in an unquoted position, so the shell splits it on IFS — space, tab, newline by default. A single line of output from find gets torn in two at the space.

Here is the measurement. Eighty 100-byte files under a directory whose name contains a space, same loop:

=== A: unquoted command substitution ===
iterations=160 readable=0 unreadable=160  (actual file count: 80)

One hundred sixty iterations — exactly twice the file count. Each path split into two fragments, and the loop treated each fragment as a filename. Neither half opens, naturally.

Switch to NUL-delimited output and the picture changes:

while IFS= read -r -d '' f; do
  head -c 1 "$f" >/dev/null 2>&1 && ok=$((ok+1))
done < <(find "$ROOT/_documents" -type f -name "*.md" -print0)
=== B: find -print0 + while read -d '' ===
iterations=80 readable=80 unreadable=0

All 80. The files had been readable the entire time.

When a fragment turns into a real path

So far this is only a missed read. What actually made me sit up was the next test.

The leading fragment of a split path can be a directory that exists.

Split /work/Dolice Labs/out/tmp_1.txt and the first half is /work/Dolice. If a folder named Dolice happens to live at that level, the fragment does not point at nothing. It points at somewhere real.

To check, I put 5 files I wanted deleted in Dolice Labs/out/ and 3 files that must survive in Dolice/keep/, then ran the naive loop with a delete in it.

for f in $(find "$T/Dolice Labs/out" -type f -name "tmp_*.txt"); do
  rm -rf "$f" 2>/dev/null
done

The result:

before: keep=3 tmp=5
after : keep=0 tmp=5
TargetIntentBeforeAfter
Dolice Labs/out/delete these55 (nothing deleted)
Dolice/keep/never touch these30 (all gone)

Not one intended file was removed, and the side that should have been untouched was wiped. The loop exited 0 and printed nothing to stderr.

Note that rm -rf "$f" is quoted. Quoting only guarantees that the fragment arrives as a single argument. It says nothing about where that fragment points. That is the core of this failure.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Be able to tell, in a few minutes, whether a batch script running under a path with spaces is quietly skipping every file
✦Be able to separate 'nothing matched' from 'nothing could be opened' mechanically, by reconciling expected and processed counts
✦Be able to decide where a guard belongs in a delete-heavy batch, based on a run that kept all 5 files it meant to delete and removed all 3 it was supposed to leave alone
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-08-31
Half of My Scheduled Runs Vanished Without a Single Error
A batch job set to run twice a day was only firing once. No errors, no failure alerts. Here is how to expand your own schedule, count expected runs, and reconcile them against execution records to catch silent misses.
⟐ Claude Code2026-08-04
Tightening Filesystem Isolation Separately from the Network — Collect the Paths, Then Squeeze the Write Surface
Claude Code v2.1.216 lets you control filesystem isolation independently from network isolation. Before tightening anything, I traced what a real job actually touches, split reads from writes, and measured how stable the path set is across repeated runs. The numbers changed how I wrote the allowlist.
⟐ Claude Code2026-09-22
Taking Inventory of Worktrees That Were Supposed to Be Gone
Isolated worktrees my subagents created were never folded away. Here is what I measured in git 2.34.1 about when remove succeeds, the branches that outlive the trees, the 12 MB per-tree difference, and how I decide who owns cleanup.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links