Problem
${var%$'\n'} and ${var%${nl}} (where nl contains a newline) fail to strip a trailing newline. The ANSI-C quoted $'\n' and variable-held newline are not interpreted correctly as the pattern in % (suffix removal) and related parameter expansion operators.
A literal embedded newline in the pattern does work: ${var%<actual newline>}.
Reproduction
str=$'abc\n'
echo "original: $(printf '%q' "${str}")"
# → $'abc\n'
# BROKEN: $'\n' in pattern
r1="${str%$'\n'}"
echo "ansi-c: $(printf '%q' "${r1}")"
# Expected: abc
# Actual: $'abc\n' (not trimmed)
# BROKEN: variable holding newline
nl=$'\n'
r2="${str%${nl}}"
echo "var: $(printf '%q' "${r2}")"
# Expected: abc
# Actual: $'abc\n' (not trimmed)
# WORKS: literal newline in pattern
r3="${str%
}"
echo "literal: $(printf '%q' "${r3}")"
# → abc ✓
Impact
wedow/harness uses ${body%$'\n'} to trim trailing newlines from message bodies when assembling conversation history:
This is also a common bash idiom used in many scripts.
Test cases
# $'\n' in % suffix removal
str=$'hello\n'
result="${str%$'\n'}"
echo "${result}"
# Expected stdout: hello
# $'\n' in %% longest suffix removal
str2=$'a\n\n'
result2="${str2%%$'\n'}"
echo "${result2}"
# Expected stdout: a
# $'\n' in # prefix removal
str3=$'\nhello'
result3="${str3#$'\n'}"
echo "${result3}"
# Expected stdout: hello
# Variable containing newline in pattern
nl=$'\n'
str4=$'world\n'
result4="${str4%${nl}}"
echo "${result4}"
# Expected stdout: world
# $'\t' in pattern (tab — related)
str5=$'data\t'
result5="${str5%$'\t'}"
echo "${result5}"
# Expected stdout: data
# Multi-char pattern with $'\n'
str6=$'line1\nline2\n'
result6="${str6%$'\n'}"
echo "$(printf '%q' "${result6}")"
# Expected: $'line1\nline2'
Problem
${var%$'\n'}and${var%${nl}}(wherenlcontains a newline) fail to strip a trailing newline. The ANSI-C quoted$'\n'and variable-held newline are not interpreted correctly as the pattern in%(suffix removal) and related parameter expansion operators.A literal embedded newline in the pattern does work:
${var%<actual newline>}.Reproduction
Impact
wedow/harness uses
${body%$'\n'}to trim trailing newlines from message bodies when assembling conversation history:body="${body%$'\n'}"This is also a common bash idiom used in many scripts.
Test cases