使用 POSIX 参数扩展从变量中删除重复的字符串模式

使用 POSIX 参数扩展从变量中删除重复的字符串模式

我想用2.6.2 参数扩展从字符串中删除前导字符,但惊讶地发现“删除最大前缀模式”不会自动重复该模式。

$ x=aaaaabc
$ printf %s\\n "${x##a}"
aaaabc

如您所见,只有第一个a已被删除。预期输出为、、或中bc的任何一个。x=bcx=abcx=aabcx=aaabcx=aaaabc

a如果我想从 的开头删除尽可能多的内容,我正在努力弄清楚如何编写该模式$x。我也没有运气搜索其他线程,因为许多答案都使用 bash,但我正在寻找 POSIX shell 解决方案。

答案1

对于某些模式,您可以通过匹配要保留的变量部分来“反转”模式:

$ for x in "" a aa abc aabc aaabc aaabca aaabcabc bc bcaa
> do
>     printf %s\\n "${x#"${x%%[!a]*}"}"
> done



bc
bc
bc
bca
bcabc
bc
bcaa

答案2

我认为你不能以通用的方式做到这一点(IE忽略模式的特定功能),仅使用 POSIX shell 构造,而不使用循环:

until [ "${x#a}" = "$x" ]; do x="${x#a}"; done

答案3

a作为模式匹配a,它不可能匹配aaa

虽然 POSIXsh规范基于 Korn shell 的子集,并且 Korn shell 具有(匹配 0 个或多个s*(foo)的序列)和运算符(匹配 1 个或多个 s 的序列,与 相同),但这些并未由POSIX,因为它们不向后兼容 Bourne shell,并且意味着在许多情况下无法使用它们,例如:foo+(foo)foofoo*(foo)

  • find . -name '*(x)'当前需要匹配以以下结尾的文件名(x)
  • pattern='*(x)'; case $file in ($pattern) ...; esac或者${file##$pattern}。相同的。您会注意到,在这些情况下 ksh88 或 pdksh 无法识别这些运算符。

正则表达式支持重复。 POSIX 指定了许多可以匹配正则表达式(expr, grep, sed, awk...)的实用程序。有些 shell 已经或已经内置了其中一些。expr内置于(或可以内置于)Almquist shell 中。可以使用,和builtinksh93构建,并且可以在不分叉的情况下获得它们的输出。某些基于 ash 的 shell 还可以在由内置命令的一次调用组成时获得命令替换的输出,而无需分叉。 shell是 shell 的另一个示例,其中所有这些实用程序都可以在不分叉或执行的情况下调用。exprgrepsedbusybox

另一方面,printf您在问题中使用的是不是内置于 ksh88 或大多数 pdksh 衍生品中。除了特殊的内置命令和诸如export/ getopts/ read... 之类的内置命令(只能合理地内置)之外,POSIX 不向您保证命令可能是内置的,也可能不是内置的。

所以:

x=$( expr "x$x" : 'xa*\(.*\)' )

a例如,可以在外壳内部剥离前导s。但有一些注意事项:

  • 如果结果是空字符串或 0 的某些表示形式,则返回失败退出状态
  • 它还会删除尾随的换行符。
  • 您会注意到x我们还需要添加前缀,以防止在$x碰巧包含expr运算符时失败(请参阅exprPOSIX规范的应用程序使用部分了解更多详情)。

或者与awk

x=$(awk 'BEGIN {sub(/^a*/, "", ARGV[1]); print ARGV[1]}' "$x")

或者sed

x=$(printf '%s\n' "$x" | sed '1s/^a*//')

sed这里最不合适,因为它基于行工作并且需要通过标准输入或文件提供输入)。

相关内容