sed Part IV · Module 12 · Engineering Case Studies and Review
Simulator Upgrade Automation
The second case study changes something executable. A simulator release removes a flag, renames an option and makes an argument mandatory, and a directory of run scripts has to move with it. That is three rules rather than one substitution, and three chained substitutions on a command line is a change nobody can review. So the edit itself becomes a file — commented, committed, and read alongside the diff it produces. The chapter ends on the property that decides whether such a sweep is safe to run twice, which matters because a tool upgrade never reaches every script on the same day.
Advanced18 min readsedPart IVCase StudySimulationScript Files
Part IV · Module 12 · Chapter 12.2 · Simulator Upgrade Automation
1. The Task
A simulator release — call it release A to release B — changes three things, chosen because each is a different shape of edit:
-full64is now the default and the flag has been removed.+v2khas been renamed to-v2k.-lis now mandatory and must name a path inside a log directory.
Two run scripts need to follow:
cat -n sim/run.sh 1 #!/bin/sh
2 vcs -full64 -sverilog +v2k -f filelists/top.f -o simv
3 ./simv +UVM_TESTNAME=smoke -l sim.logsim/run_stress.sh is a copy with the same invocation. In a real flow there would be forty of them, which is the point.
2. Why This One Is a Script File
Chapter 12.1's migration was a single substitution, and a one-liner was the right form for it. Three rules is different:
sed -e 's/ -full64//' -e 's/+v2k/-v2k/' -e 's|-l sim\.log|-l logs/sim.log|' …That runs correctly and is close to unreviewable. Nothing in it says why -full64 is being deleted, a reviewer cannot tell a removal from a typo, and the next release's change gets appended to a line that is already too long.
Chapter 6.4 made the general case for -f files. Here the deciding factors are that the edit has more than one rule, will be run across many files, and needs to be reviewed by someone who does not know the release notes — which is what the per-rule comments are for.
3. The Upgrade Script
cat -n scripts/vcs-upgrade.sed 1 # vcs-upgrade.sed — release A -> release B (FICTIONAL scenario, see §1)
2 # 1. -full64 is now the default and the flag was removed
3 s/ -full64//
4 # 2. +v2k was renamed
5 s/+v2k/-v2k/
6 # 3. -l is now mandatory and must name a directory
7 s|-l sim\.log|-l logs/sim.log|Seven lines, three of them commands. Each rule carries the reason for itself, which is the thing a command line cannot do.
Three details worth naming:
- Rule 1 deletes the leading space too —
s/ -full64//rather thans/-full64//. Without the space the line would be left with a double space where the flag was. A small thing that shows up in every diff for years. - Rule 2 relies on basic mode.
+v2khas a literal+, which Chapter 10.1 measured failing two different ways under-E— here the+is leading, so-Ewould be a hard error. A script file is read by sed directly with no-E, so basic mode is the default and the right one. - Rule 3 escapes the dot.
sim\.lograther thansim.log, because an unescaped dot matches any character — Chapter 3.3. It also uses|as the delimiter since the replacement contains a path.
And the file is committed. The diff it produces and the rules that produced it are reviewed together, and next year's upgrade is a new file next to this one rather than an edit to shell history.
4. Dry Run
sed -f scripts/vcs-upgrade.sed sim/run.sh | diff -u sim/run.sh ---- sim/run.sh 2026-10-12 23:42:05
+++ - 2026-10-12 23:43:29
@@ -1,3 +1,3 @@
#!/bin/sh
-vcs -full64 -sverilog +v2k -f filelists/top.f -o simv
+vcs -sverilog -v2k -f filelists/top.f -o simv
-./simv +UVM_TESTNAME=smoke -l sim.log
+./simv +UVM_TESTNAME=smoke -l logs/sim.logTwo changed lines, and all three rules are visible in them: -full64 gone with no leftover double space, +v2k now -v2k, and the log path inside logs/. The shebang is untouched.
This is the artifact a reviewer reads, next to the script that produced it. Together they answer "what changed" and "why" without anyone having to ask.
5. The Sweep
Chapter 8.3's procedure: build the list, read it, then use it.
find sim -name 'run*.sh' -print0 | xargs -0 -n1 echosim/run.sh
sim/run_stress.shTwo scripts, and run_stress.sh is the one you would have missed by running the command on the file you happened to have open. Then apply:
find sim -name 'run*.sh' -print0 | xargs -0 sed -i '' -f scripts/vcs-upgrade.sed
git diff --stat sim/run.sh | 4 ++--
sim/run_stress.sh | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)Two files, four lines — two per script, matching the dry run. And both really were updated:
grep -n 'vcs ' sim/*.shsim/run.sh:2:vcs -sverilog -v2k -f filelists/top.f -o simv
sim/run_stress.sh:2:vcs -sverilog -v2k -f filelists/top.f -o simv-print0 with -0 because Chapter 8.3 measured an ordinary filename with a space turning one file into three that do not exist, and -i '' is the BSD spelling from Chapter 7.1.
6. Idempotency — the Property That Makes It Safe to Repeat
A tool upgrade does not land everywhere at once. Some scripts get converted by hand, some are created after the sweep, someone re-runs it to catch stragglers. So the question is not only "does it work" but "what does it do to a file that is already correct?"
Run it a second time on the same script:
cp sim/run.sh /tmp/once.sh
sed -i '' -f scripts/vcs-upgrade.sed sim/run.sh
diff -u /tmp/once.sh sim/run.sh && echo 'IDEMPOTENT — second run changed nothing'IDEMPOTENT — second run changed nothingAnd against a script that was already on the new release:
printf '#!/bin/sh\nvcs -sverilog -v2k -f filelists/top.f -o simv\n./simv -l logs/sim.log\n' > /tmp/new.sh
sed -f scripts/vcs-upgrade.sed /tmp/new.sh | diff -u /tmp/new.sh - && echo 'UNCHANGED — safe to re-run across a mixed tree'UNCHANGED — safe to re-run across a mixed treeBoth clean. The sweep can be re-run over a half-converted directory without damaging the half that was already done.
A mixed tree — one old script, one already converted — is the realistic case, and it behaves:
sed -i '' -f scripts/vcs-upgrade.sed mixed/*.sh
cat mixed/old.sh#!/bin/sh
vcs -sverilog -v2k -f top.f -o simv
./simv -l logs/sim.logThe old one was upgraded, and the already-converted one came back byte-identical by checksum. So the sweep can be pointed at the whole directory without sorting it first.
Idempotent is not the same as correct
Now the test that matters more, and that the first two do not cover. Give the sweep a script where the option text appears somewhere it is not an option:
cat mixed/ctx.sh#!/bin/sh
# we removed -full64 because it is now default
echo "use +v2k for the old parser"
vcs -full64 -sverilog +v2k -f top.f -o simvsed -f scripts/vcs-upgrade.sed mixed/ctx.sh#!/bin/sh
# we removed because it is now default
echo "use -v2k for the old parser"
vcs -sverilog -v2k -f top.f -o simvThe invocation on line 4 is correct. The other two lines are not:
- The comment now reads "we removed because it is now default" — the rule deleted the flag name out of a sentence, leaving a grammatical fragment that says nothing.
- The string in the
echowas rewritten, so the script now prints advice about an option under its new name — a change to what the program outputs, not to what it does.
Both survive a second run unchanged, so the script is still idempotent. Idempotency says a transformation is stable under repetition; it says nothing about whether the transformation was right. This one is stable and wrong on two of four lines.
That is Chapter 9.3's boundary — sed matches characters, not roles — arriving in a shell script rather than in RTL. The defence is the same: scope the rules to the invocation line, as Chapter 10.1 showed, and read the diff for comment and string hits rather than only checking that the sweep converged.
7. Think It Through
1. When does a multi-rule edit stop being a one-liner?
When a reviewer would have to ask why. Three chained -e fragments run fine and carry no reasons; a -f file gives each rule a comment, a diff when it changes, and a place for next year's rules. Chapter 6.4's other triggers apply too — a label makes the file mandatory, since Chapter 6.3 measured the compact label form failing silently on BSD.
2. Why s/ -full64// rather than s/-full64//?
To take the separating space with the flag. Without it the line keeps a double space where the option was — harmless to the shell, visible in every diff of that file from then on, and the kind of thing that makes a reviewer wonder whether the edit was careful.
3. Why does rule 2 not need -E, and what would happen if the script were run with it?
+v2k has a literal leading +. Under -E that is a repetition operator with nothing to repeat, which Chapter 10.1 measured as repetition-operator operand invalid, exit 1. A -f script is read in basic mode unless you add -E on the command line, so the right answer is simply not to.
4. Your upgrade script is run twice by mistake. How would you know whether that mattered?
Diff the result against the once-upgraded version — section 6 does exactly this and got no difference. If you cannot run that test, read each rule and ask whether its replacement could still match its own pattern. The dangerous shapes are rules that add something: a prefix, a directory, a flag.
5. The sweep is clean and the diff is right. Is the upgrade done?
No. Nothing here executed a simulator, so what has been verified is that the text now matches the change the scenario specifies. The sufficient test is one smoke run on the new tool — and the reason the text work still matters is that it makes that smoke run test the upgrade rather than a typo.
6. Forty scripts, and three of them were hand-edited last week. What changes?
Nothing about the command, because of section 6 — the sweep leaves already-correct scripts alone. What changes is that you read the file list first (section 5) and check git diff --stat reports 37 files rather than 40. A count that is lower than the file count is the expected result here, and knowing that beforehand is what stops it looking like a failure.
8. Summary
- Three rules is not a one-liner. A removed flag, a renamed option and a newly mandatory argument belong in a committed
-ffile where each rule carries its reason. - Rule 1 deletes the leading space with the flag, or every future diff shows a double space.
- Rule 2 relies on basic mode — a leading
+is an error under-E(Chapter 10.1). - Rule 3 escapes the dot and uses
|as the delimiter. - Dry-run as a diff: 2 changed lines showing all three rules at once, reviewed alongside the script that produced them.
- Sweep NUL-safely: read the list, then apply.
git diff --statgave 2 files, 4 lines — matching the dry run. - Measure idempotency, twice: a second run changed nothing, and an already-upgraded script was left untouched. A sweep is only safe to repeat if that holds.
- Idempotency is a property of the rules.
s|sim\.log|logs/sim.log|without the-lcontext would have producedlogs/logs/sim.logon the second run. - Text-correct is necessary, not sufficient. One smoke run on the new release is the real test; the sweep makes that run meaningful.
Both case studies so far went well, because both followed the procedure. The next one did not. Chapter 12.3 — Diagnosing a Dangerous Mass Replacement reconstructs an incident from its diff: a single unanchored rename, run with -i across a tree, that reported success, still compiled, and was wrong.
Where this fits
Part of the sed curriculum.
