我有一个搜索大量文件的脚本,并使用sed替换多行模式。该脚本是迭代的,它在某些迭代中工作正常但有时会导致分段错误。
这是脚本正在做的事情:
模式A,B,C,D是多行的,它们被两行代替。 X和Y是单行。
这是脚本。我为长队道歉,但我决定不编辑它们,因为它们是正则表达式。然而,我通过用“pattern”替换字符串来缩短正则表达式 - 在每个正则表达式中替换的内容都不相同,但它们没有任何特殊字符,因此我认为实际内容与此问题无关。此外,正则表达式已被证明有效,所以你可能不需要完全理解它..
#!/bin/sh
STRING_A="Pattern(\n|.)*Pattern\.\""
A_TAG="\$STRING:A$"
STRING_B="(Pattern(\n|.)*)?(Pattern(\n|.)*)?Pattern(\n|.)*Pattern(\n|.)*Pattern\.((\n|.)*will be met\: http\:\/\/www.foo\.org\/example\/temp\.html\.\n)?"
B_TAG="\$STRING:B$"
STRING_C="(Pattern(\n|.)*)?Pattern(\n|.)*http\:\/\/www\.foo\.org\/bar\/old-foobar\/file\-2\.1\.html\.((\n|.)*Pattern.*Pattern)?"
C_TAG="\$STRING:C$"
STRING_D="(Pattern(\n|.)*)?(Pattern(\n|.)*http\:\/\/www\.foo\.org\/bar\/old-foobar\/file\-2\.1\.html.*|Pattern(\n|.)*Pattern)((\n|.)*http\:\/\/www\.some-site\.org/\.)?"
D_TAG="\$STRING:D$"
## params: #1 file, #2 PATTERN, #3 TAG
multil_sed()
{
echo "In multil_sed"
# -n = silent, -r = extended regex, -i = inline changes
sed -nr '
# Sed has a hold buffer that we can use to "keep text in memory".
# Here we copy the line to the buffer if it is the first line of the file,
# or append it if it is not
1h
1!H
# We must first save all lines until the nth line to the hold buffer,
# then we can search for our pattern
60 {
# Then we must use the pattern buffer. Pattern buffer holds text that
# is up for modification. With g we can hopy the hold buffer into the pattern space
g
# Now we can just use the substitution command as we normally would. Use @ as a delimiter
s@([ \t:#*;/".\\-]*)'"$2"'@\1'"$3"'\
\1$QT_END_LICENSE$@Ig
# Finally print what we did
p
}
' $1 > $1.foo;
echo "Done"
}
for p in $(find . -type f -not -iwholename '*.git*' -exec grep -iL '.*STRING_X.*' {} \; | xargs grep -il -E '.*STRING_Y.*')
do
echo
echo "####################"
echo "Working on file" $p
#Find A
if pcregrep -qiM "$STRING_A" "$p";
then
echo "A"
multil_sed "$p" "$STRING_A" "$A_TAG"
#Find B
elif pcregrep -qiM "$STRING_B" "$p";
then
echo "B"
multil_sed "$p" "$STRING_B" "$B_TAG"
#Find C
elif pcregrep -qiM "$STRING_C" "$p";
then
echo "C"
multil_sed "$p" "$STRING_C" "$C_TAG"
#Find D
elif pcregrep -qiM "$STRING_D" "$p";
then
echo "D"
multil_sed "$p" "$STRING_D" "$D_TAG"
else
echo "No match found"
fi
echo "####################"
done
我应该注意到C本质上是D的较长版本,在公共部分之前有一些额外的内容。
对于某些迭代,会发生什么呢?
####################
Working on file ./src/listing.txt
A
In multil_sed
Done
####################
有时它没有。
####################
Working on file ./src/web/page.html
/home/tekaukor/code/project/tag_adder.sh: line 54: 16904 Segmentation fault (core dumped) pcregrep -qiM "$STRING_A" "$p"
No match found
####################
它不依赖于搜索的模式。
####################
Working on file ./src/test/formatter_test.cpp
/home/tekaukor/code/project/tag_adder.sh: line 54: 18051 Segmentation fault (core dumped) pcregrep -qiM "$STRING_B" "$p"
/home/tekaukor/code/project/tag_adder.sh: line 54: 18053 Segmentation fault (core dumped) pcregrep -qiM "$STRING_C" "$p"
/home/tekaukor/code/project/tag_adder.sh: line 54: 18055 Segmentation fault (core dumped) pcregrep -qiM "$STRING_D" "$p"
No match found
####################
第54行指向“for $ in $(找。-type f -not -iwholename' .git ' - exec grep ...”。
我的猜测是sed导致缓冲区溢出,但我还没有办法确定或解决这个问题。
答案 0 :(得分:2)
Bash在复合语句中查找错误来源并不是很好
第54行指向
for p in $(find . -type f ...
行。
具有误导性,因为错误可能位于语句块的任何位置。错误消息
分段错误(核心转储)pcregrep -qiM“$ STRING_D”“$ p”
准确得多。可能导致错误的原因是-M
标志与无限制模式相结合,如(.|\n)*
作为pcregrep man page注释:
-M, - multiline 允许模式匹配多行。给出此选项时,模式可能有用地包含文字换行符和^和$字符的内部出现。任何一个匹配的输出可能包含多个行。设置此选项后,将以“多行”模式调用PCRE库。 可以匹配的行数有限制,这是由pcregrep在扫描输入文件时缓冲输入文件的方式强加的。但是,pcregrep确保至少8K字符或文档的其余部分(以较短者为准)可用于前向匹配,类似地,之前的8K字符(或所有先前字符,如果少于8K)是保证可用于外观断言。
强调我的。单个模式片段.*
或(.|\n)*
可以完全匹配整个文件,所以是的,它将填充其前瞻缓冲区,而不仅仅是下一个文字(例如http
),但直到它找到最后一个这样的文字,因为默认正则表达式寻找最长的符合匹配。
答案 1 :(得分:1)
UPDATE#2:显然sed不支持非贪婪匹配,这使我的部分答案无效。有很多方法,但我不会在这里包括它们,因为它与原始问题相去甚远。这个问题的答案是使用--disable-stack-for-recursion标志,如下所述。
msw的回答帮助我朝着正确的方向前进。
首先,我将正则表达式改为懒惰而不是贪婪。默认情况下,regex是贪婪的,(如msw所述)意味着带有“PATTERN(。| \ n)* TEXT”的多行表达式将搜索整个文件。通过增加 ”?”在量词之后(* - > *?)我让regez懒惰,这意味着“(。| \ n)*?”在“PATTERN(。| \ n)*?TEXT”将停止在第一个TEXT扩展。
我还将可选部分设为懒惰(? - > ??),但我不确定这是否必要。
然而这还不够。我还必须配置pcregrep来使用堆而不是堆栈内存。我下载了pcre并使用标志--disable-stack-for-recursion进行配置。请注意,使用堆要慢得多,所以如果不必这样做,就不应该这样做。
如果有人在这里遇到同样的问题,我会逐步介绍。请注意,我仍然是一个Linux新手,并且很有可能我做了一些不必要和/或愚蠢的事情。说明基于http://www.mail-archive.com/pcre-dev@exim.org/msg00817.html和http://www.linuxfromscratch.org/blfs/view/svn/general/pcre.html
提供的指南中有一些额外的步骤,但我没有必要这样做。
更新:使可选元素变得懒惰(? - > ??)是一个错误,因为如果可能,它们将不会包含在匹配的模式中。