uutils/sed

Add `\U`/`\L`/`\u`/`\l`/`\E` case-folding in replacement

Open

#400 opened on 2026年4月30日

GitHub で見る
 (0 comments) (1 reaction) (0 assignees)Rust (23 forks)github user discovery
good first issue

Repository metrics

Stars
 (91 stars)
PR merge metrics
 (平均マージ 6d 9h) (30d で 25 merged PRs)

説明

Bug

The GNU case-folding escapes inside the s/// replacement — \U / \L / \u / \l / \E — are not supported. Today, they are passed through literally as the letter after the backslash.

Reproduction

$ echo abcDEF | /usr/bin/sed 's/.*/\U&/'
ABCDEF

$ echo abcDEF | ./target/release/sed 's/.*/\U&/'
UabcDEF      # \U treated as literal 'U'
$ echo ABCDEF | /usr/bin/sed 's/.*/\l&/'
aBCDEF

$ echo ABCDEF | ./target/release/sed 's/.*/\l&/'
\lABCDEF     # \l passed through with the backslash

Same issue for \L (all lowercase until \E or end), \u (uppercase next char only), \E (end the previous \U/\L).

What it should do

GNU semantics:

Escape Meaning
\U uppercase all output until \E or end of replacement
\u uppercase the very next output character
\L lowercase all output until \E or end
\l lowercase the very next output character
\E terminate the effect of the most recent \U/\L

These nest and apply to text from backreferences (\1, &) as well as literal text in the replacement.

Suspected place to add it

src/sed/compiler.rs:649compile_replacement builds the replacement template (the structure that processor.rs renders). Add new template-element variants for each case-folding state, then have the renderer track a small "case mode" stack while writing output.

Look at how \1/& are handled (around compiler.rs:1144 and the test at compile_replacement_backrefs_and_literal) — that's the pattern to mirror.

Note: the test subst-replacement.sh also checks that \E ends a range, that \U\1\E\2\3 only uppercases the first group, etc.

Affected GNU testsuite tests

subst-replacement, posix-mode-s (where the absence of \l is the expected POSIX behavior — i.e. the GNU branch must produce different output from the POSIX branch). The fix should be gated by "not --posix" so existing POSIX-mode pass-through is preserved.

コントリビューターガイド