在bash中最优地替换多行子串以进行非贪婪匹配?

时间:2017-08-02 06:36:34

标签: string bash replace split

对于单行替换字符串有很多不同的解决方案,但我很好奇最有效的方法是使用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,这可能看起来很简单,但这两个工具只在各行上执行匹配,不适合多行匹配。

1 个答案:

答案 0 :(得分:0)

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

echo "${to_search//$to_find/$to_replace}"

a
b
c
-
a
b
c
-
a
b
c

编辑:根据您编辑的包含全局字符*的示例,您可以使用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