最佳地替换 bash 中的多行子字符串以实现非贪婪匹配?



单行字符串替换有很多不同的解决方案,但我很好奇仅使用bash内置的执行多行替换的最有效方法是什么。

我正在使用 bash 的内置正则表达式使用以下解决方案,如果只有一个匹配项,它可以正常工作:

read -r -d '' to_search <<'EOF'
1
2
3
-
1
2
3
-
1
2
3
EOF
read -r -d '' to_find <<'EOF'
1
2
3
EOF
read -r -d '' to_replace <<'EOF'
a
b
c
EOF
[[ $to_search =~ (.*)($to_find)(.*) ]] &&
echo "${BASH_REMATCH[1]}$to_replace${BASH_REMATCH[3]}"

如果我想替换所有匹配项,这也可以工作,因为我可以循环直到没有匹配项。但它没有提供非贪婪的解决方案,因为 bash 的正则表达式内置不支持?运算符。

例如,由于匹配是贪婪的,因此输出将仅考虑最后一个匹配项,而不是在第一个匹配项停止。例如:

1
2
3
-
1
2
3
-
a
b
c

一种解决方案可能是将输入和匹配字符串拆分为数组,并在循环中单步执行它们以查找匹配项,但这可能不是最佳的。

我欢迎其他解决方案供参考,但具体问题是仅使用 bash 内置来解决这个问题。这可以通过将字符串传递给Python或Perl来解决,但这不是我想要的。

欢迎使用通用实用程序(sed/grep/awk(的替代方案提供答案以供参考和比较,但不会接受,因为它没有回答这个特定问题。适用于不提供正则表达式设施的较旧 bash 环境的解决方案的额外布朗尼点。

请注意,对于 sed 和 grep,这乍一看可能很容易,但这两种工具都只在单个行上执行匹配,不适合多行匹配。

如果它涉及纯文本搜索或 glob 模式,那么 BASH 的字符串替换工作正常:

echo "${to_search//$to_find/$to_replace}"
a
b
c
-
a
b
c
-
a
b
c
编辑

根据您编辑的包含 glob 字符*的示例,您可以使用index函数使用此 awk 搜索循环:

read -r -d '' to_search <<'EOF'
*
1
2
3
*
1
2
3
*
1
2
3
EOF
read -r -d '' to_find <<'EOF'
*
1
2
3
EOF
read -r -d '' to_replace <<'EOF'
a
b
c
EOF
awk -v s="$to_search" -v f="$to_find" -v r="$to_replace" 'BEGIN {
while(p=index(s, f)) s = substr(s, 1, p-1) r substr(s, p+length(f)); print s}'
a
b
c
a
b
c
a
b
c

PS:请注意,以下bash模式替换也可以通过转义每个*来工作:

echo "${to_search//${to_find//*/\*}/$to_replace}"
a
b
c
a
b
c
a
b
c

最新更新