bash sed(或其他)替换前面锚定的双 space 和尾随不可打印字符

bash sed (or others) to substitute front anchored double space AND trailing non-printable character

我有以下形式的行

line="  this is a line with 2 leading spaces and a trailing control char^M"

我想用 ^M 代替 2 个前导空格和尾随控制字符。

echo "${line}" | sed 's/^[[:space:]]*//' | tr -dc '[:print:]'
echo "${line}" | sed 's/^[[:space:]]*//' | sed 's/[^[:print:]]//'

两者都有效。我也试过

echo "${line}" | sed 's/^[[:space:]]*|[^[:print:]]//'

但这不起作用。

为什么最后一个表达式不起作用?
如何通过一次调用 sed 和一个正则表达式来完成此操作?
首选解决方案是什么,例如在效率方面?避免使用许多子外壳会更好吗?
有没有更好的解决方案?

这个单一的 sed 应该可以工作:

sed 's/^[[:blank:]]*//; s/[[:cntrl:]]*$//' <<< "$line"
this is a line with 2 leading spaces and a trailing control char
sed 's/^[[:space:]]*|[^[:print]]//'

不起作用,因为 | 按字面意思匹配自身。 "Or" 在 sed 中拼写为 \|。 (而 [:print] 应该是 [:print:])。

但这还不够,因为默认情况下 sed 只替换第一次出现的;您需要 /g 标志来替换所有出现的地方:

sed 's/^[[:space:]]*\|[^[:print:]]//g'

但是您的原始正则表达式可能会产生一些意想不到的后果:[[:space:]] 匹配换行符,因此如果输入是一个或多个完整的行,它将删除所有空行,而不仅仅是它们的内容。为防止这种情况,请改用 [[:blank:]]

sed 's/^[[:blank:]]*\|[^[:print:]]//g'