我有一个脚本,它搜索大量的文件,并使用use来替换多行模式。脚本是迭代的,它在一些迭代中工作得很好,但有时它会导致分段错误。
这就是脚本所做的:
- 查找不包含字符串X的文件
- 在这些文件中,搜索包含字符串Y 的文件。
- 使用for循环迭代返回的文件列表
- 如果文件内容匹配模式A,则将模式A替换为A_TAG
- 模式B、C、D相同(一个文件只能包含a、B、C、D中的一个)
模式A,B,C,D是多行,它们被替换为两行。X和Y是一条线
这是脚本。我为长行道歉,但我决定不编辑它们,因为它们是正则表达式。然而,我确实通过用"模式"替换字符串来缩短正则表达式-替换的内容在每个正则表达式中都不相同,但它们没有任何特殊字符,所以我认为实际内容与这个问题无关。此外,regex已经被证明可以工作,所以你可能不需要完全理解它。
#!/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 p in $(find .) "行。-type f -not -iwholename '。Git ' -exec grep…"。
我猜是sed导致缓冲区溢出,但我还没有找到一种方法来确定或解决这个问题。
Bash不擅长定位复合语句中的错误来源,所以
第54行指向
for p in $(find . -type f ...
行。
会产生误导,因为错误可能在for语句块中的任何地方。错误信息
段错误(core dump) pcregrep -qiM "$STRING_D" "$p"
更为准确。故障的原因可能是-M
标志与(.|n)*
等无界模式相结合,正如pcregrep手册页所指出的:
- m,多行允许模式匹配多个行。当给出此选项时,模式可能有用地包含字面换行字符和内部出现的^和$字符。任何匹配的输出都可以包含多行。当设置此选项时,以"多行"模式调用PCRE库。可以匹配的行数是有限制的,这是由pcregrep在扫描输入文件时缓冲输入文件的方式决定的。然而,pcregrep确保至少有8K个字符或文档的其余部分(以较短的为准)可用于前向匹配,类似地,保证前面的8K个字符(或所有前面的字符,如果少于8K)可用于向后查找断言。
强调我的。单个模式片段.*
或(.|n)*
可以从字面上匹配整个文件,所以是的,它将填充其前瞻性缓冲区,不仅到下一个文字(例如http
),而且直到它找到最后一个这样的文字,因为默认情况下正则表达式查找最长的符合匹配。
UPDATE #2:所以显然sed不支持非贪婪匹配,这使得我的部分回答无效。有一些方法可以解决这个问题,但我不会在这里包括它们,因为它与原始问题相去甚远。这个问题的答案是使用——disable-stack-for- recursive标志,如下所述。
msw的回答帮我找到了正确的方向。
首先,我将正则表达式更改为lazy而不是greedy。默认情况下,regex是贪婪的,这(如msw所述)意味着带有"PATTERN(.|n)*TEXT"的多行表达式将搜索整个文件。通过在量词后添加"?"(* -> *?)我将regez设置为惰性,这意味着"PATTERN(.|n)*?"中的"(.|n)*?"TEXT"将在第一个TEXT处停止展开。
我还使可选部分懒惰(?-> ?? ?),尽管我不确定这是否必要。
然而这还不够。我还必须将pcregrep配置为使用堆而不是堆栈内存。我下载了pcre并使用——disable stack-for-递归标志进行了配置。请注意,使用堆的速度要慢得多,所以如果没有必要,就不应该这样做。
我附上了一个步骤,以防有人在这里遇到同样的问题。请注意,我仍然是一个linux新手,我很有可能做了一些不必要的和/或愚蠢的事情。说明基于http://www.mail-archive.com/pcre-dev@exim.org/msg00817.html和http://www.linuxfromscratch.org/blfs/view/svn/general/pcre.html
- 从http://downloads.sourceforge.net/pcre/pcre-8.33.tar.bz2下载 tar.bz2
- cd pcre - 8.33
- 。/configure——prefix=/usr——docdir=/usr/share/doc/pcrep -8.33——enable-utf——enable-unicode-properties——enable-pcregrep-libz2——disable-static——disable-stack-for-递归
- 使 sudo make install
在提供的指南中有一些额外的步骤,但我不必做它们。
更新:将可选元素设置为惰性(?)-> ??)是错误的,因为如果可能的话,它们将不包含在匹配的模式中。