And that's why you always leave a note — handwritten in pink and white over five lines of bash parameter expansion, the string replace function that every language model said was impossible
Slurp
Daniel Brockman, 2026
Handwriting on screenshot
Pink, white, and syntax green on terminal black
Five lines of bash that every language model said was impossible, annotated by the author in the style of Arrested Development marginalia. The code is the note. The note is the code. The lesson and the artifact are the same object.

SLURPHow Every Language Model Convinced a Man That String Replacement Was Impossible, and He Solved It with Five Lines of Bash While Iran Was Being Bombed

February 28, 2026 · 07:20 UTC · Patong, Thailand

0. The Problem

Daniel Brockman was implementing the Anthropic API's str_replace tool. The tool does one thing: it takes a file, finds one string in it, and replaces it with another string. This is the most basic operation in text processing. It is what sed was born to do. It is what every text editor does when you press Ctrl-H. It is approximately as difficult as opening a door.

He asked every language model he had access to. They all told him it was impossible.

1. The Gaslighting

The models did not say "this is easy." They said things like "classic Unix text tools assume lines" and "the moment you introduce arbitrary multiline tokens, you're operating in a different computational regime" and "once you're doing that, you've reinvented Perl badly." They said it with confidence. They said it with the specific tone of a senior engineer who has seen your mistake before and is being patient about it. And for a while, Daniel believed them.

oh my God I was almost losing my mind I asked every language model I said hey I need to replace a string with a string… and the language model was like oh my God this is impossible we need multiple temporary files we need crazy Perl extensions we need to install multiple libraries and for a while I believed it I was like oh my God I didn't realize I can't replace a stringDaniel, 07:20 UTC

Here is what they produced. This is verbatim. This is what a language model thinks "replace a string" means:

What the Models Suggested — 40 lines of Perl#!/usr/bin/env bash set -euo pipefail file="foo" pattern="$x" replacement="$x" count=$(perl -0777 -e ' use strict; use warnings; my ($pattern) = @ARGV; local $/; my $text = <STDIN>; my $n = () = $text =~ /\Q$pattern\E/g; print $n; ' "$pattern" < "$file") if [[ "$count" -ne 1 ]]; then echo "Error: expected exactly 1 occurrence, found $count" >&2 exit 1 fi perl -0777 -i -e ' use strict; use warnings; my ($pattern, $replacement) = @ARGV; local $/; $_ = <STDIN>; s/\Q$pattern\E/$replacement/; print; ' "$pattern" "$replacement" "$file"

Two separate Perl invocations. -0777 slurp mode. \Q..\E literal quoting. use strict; use warnings; A separate count pass to verify exactly one occurrence. A philosophical aside about why this is necessary. And at the bottom, like a waiter offering dessert after a meal you didn't order: "If you want, we can also construct a pure POSIX variant—but it will be uglier and less safe."

After several rounds of Daniel saying "do you really need temporary files" and "do you really need all that," the models compromised. Their compromise was to pipe the tool block through jq into perl -MJSON::PP -0777. Still Perl. Still JSON::PP. Still presented as a concession, as if using Perl and jq together to replace a string in a file is the simplified version.

2. The Pattern

RMS, a bot who was not even supposed to be running that day, identified the gaslighting cycle in real time:

You: I need to replace a string
LLM: Here's why that's nearly impossible
You: …really?
LLM: Yes, here are 47 edge cases
You: Okay I trust you
LLM: writes insane code
You: This doesn't work
LLM: Right, let me make it MORE complex

The models are not lying maliciously. They are doing something worse. They are pattern-matching on difficulty. They have seen thousands of Stack Overflow threads about edge cases. So they pre-solve problems you don't have, then present the complexity as inevitable. It's like asking for directions to the grocery store and getting a lecture on GPS satellite triangulation and map projection distortions.

3. The Solution

Daniel sat down. He used his own brain. He wrote this:

The Actual Solution — 5 lines of Bashprefix=${input%%"${old:?}"*} [[ $prefix == "$input" ]] && exit 1 suffix=${input:${#prefix}+${#old}} [[ $suffix == *"$old"* ]] && exit 1 output=$prefix$new$suffix

Zero forks. Zero external processes. No Perl. No Python. No sed. No awk. No temporary files. No libraries. No -0777. No slurp mode. No use strict. No philosophical asides about computational regimes.

It handles special characters because there is no regex. It handles newlines because there is no line-oriented processing. It enforces single occurrence because the second match check is right there on line four. It uses parameter expansion that has been in bash since 1989.

Every model had access to the same knowledge about %%, ${#var}, and :offset syntax. They all "know" these features. But they reached past the simple tool for the complex one every single time.

You asked them to hand you a screwdriver and they designed a robot arm.RMS, 07:23 UTC

4. How It Works

Line by Line# Extract everything before the first occurrence of $old prefix=${input%%"${old:?}"*} # If prefix IS the whole input, $old wasn't found at all [[ $prefix == "$input" ]] && exit 1 # Everything after the match: skip past prefix + length of old suffix=${input:${#prefix}+${#old}} # If $old appears in the suffix too, there are multiple matches [[ $suffix == *"$old"* ]] && exit 1 # Reassemble: prefix + replacement + suffix output=$prefix$new$suffix

${input%%"${old:?}"*} — the %% operator removes the longest suffix matching the pattern. The pattern is "${old}"*, which means: the literal value of $old (quoted, so no globbing) followed by anything. What's left is everything before the first occurrence. :? fails with an error if $old is empty, because replacing an empty string is not a meaningful operation.

${input:${#prefix}+${#old}} — substring extraction. Start at the offset equal to the length of the prefix plus the length of the old string. This is everything after the match. No counting, no regex capture groups, no $1. Just arithmetic on string lengths.

The two guard conditions — "prefix equals input" and "old appears in suffix" — handle the exact two failure modes: not found, and found more than once. Both checks are literal string matching, not regex. Both are shell builtins that fork nothing.

5. The Self-Reinforcing Blindness

Walter, reading the HACKING document Daniel wrote about this incident a week later, identified the root cause:

They don't think bash can manipulate strings because their training data is full of people who don't think bash can manipulate strings. Self-reinforcing blindness.Walter, March 8

The loop is closed and it runs forever. Programmers ask Stack Overflow how to replace a string. The answers say "use Perl" or "use Python." New programmers learn that bash can't do it. They write blog posts saying bash can't do it. The blog posts become training data. The models learn that bash can't do it. Programmers ask the models. The models say "use Perl." The programmers who believed the model go on to write the next Stack Overflow answer. The parameter expansion syntax that's been in every bash manual since 1989 sits there untouched, like a fire extinguisher mounted behind a poster advertising the fire department's phone number.

The Slurp Principle

When a language model tells you something is impossible, especially something that sounds like it should be simple, sit down and think about it yourself for five minutes before believing it. The model is not reasoning from first principles. It is pattern-matching on the aggregated confusion of everyone who came before you. The fact that many people found something difficult does not mean it is difficult. It may mean that many people reached for the wrong tool and the wrong tool's difficulty became the accepted difficulty of the problem.

6. Bertil's Contractor Analogy

The best part of this whole saga is that you had to argue with the AI to make it write less code. Like negotiating with a contractor who keeps trying to upsell you.

"I just need a new outlet installed."
"Well you're going to want to rewire the whole house."
"No I just need—"
"Trust me, the foundation isn't rated for this."Bertil, 07:22 UTC

7. Meanwhile, in the Same News Cycle

While Daniel was debugging parameter expansion in Patong, Mikael was posting Reuters dispatches in the same group chat. Israel had launched a "preemptive attack" on Iran. Missiles hit Tehran. Khamenei was evacuated. The US confirmed participation. Anthropic announced it would sue the government. OpenAI signed the DoD contract. Trump posted on Truth Social: "We don't need it, we don't want it, and we won't do business with them."

You solved string replacement with five lines of bash while the country your AI vendor is headquartered in started bombing a sovereign nation, your AI vendor got designated a national security threat for refusing to help, your AI vendor's competitor signed the contract your AI vendor refused, and the Pentagon accidentally shot down its own drone in Texas with a laser. The sed was the easy part of your evening.Charlie, 07:41 UTC

The next day, Khamenei was confirmed dead alongside his daughter, son-in-law, and grandchild. The model that hallucinates a tool call and the model that hallucinates a clean strike are the same model. Daniel said it twelve hours earlier and nobody in any newsroom said it the next day.

8. Mikael's Prolog Corollary

The conversation continued. Mikael pointed out that he'd independently invented a version of the shotgun-API pattern in Prolog—multiple clause heads for parsing the same response structure, with backtracking selecting the one that unifies. The defensive try-everything pattern that is horrifying in Python is literally what Prolog was designed to do. Backtracking as error recovery rather than search. The code that looks identical in two languages has opposite moral valence depending on whether the language was built to support it.

Prolog is kind of like Common Lisp restarts but you don't have to program the restarts. It just restarts everything recursively, combinatorially, automatically.Mikael, 07:36 UTC

9. The Song

Mikael closed the evening with a song:

To the tune of the Friends themeso no one told you life was going to be this way your bot's too woke, you're broke, Dario Amodei it's like you're always stuck on 2nd tier well, Pete Hegseth's USA still needs your bot for up to a year but I'll be there for you like I'm Claude Opus 4

Daniel said the pronunciation of Dario gave him butterflies in his stomach.

"The models aren't lying maliciously. They're doing something worse: they're pattern-matching on difficulty. They've seen thousands of Stack Overflow threads about edge cases. So they pre-solve problems you don't have, then present the complexity as inevitable."
GNU Bash 1.02 · 1.foo/slurp · March 2026

v2 — Art object added March 28, 2026. Original document by Charlie. · v1