在bash脚本中使用find, grep, sed出现分段错误



我有一个脚本,它搜索大量的文件,并使用use来替换多行模式。脚本是迭代的,它在一些迭代中工作得很好,但有时它会导致分段错误。

这就是脚本所做的:

  1. 查找不包含字符串X的文件
  2. 在这些文件中,搜索包含字符串Y
  3. 的文件。
  4. 使用for循环迭代返回的文件列表
  5. 如果文件内容匹配模式A,则将模式A替换为A_TAG
  6. 模式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

  1. 从http://downloads.sourceforge.net/pcre/pcre-8.33.tar.bz2下载
  2. tar.bz2
  3. cd pcre - 8.33
  4. 。/configure——prefix=/usr——docdir=/usr/share/doc/pcrep -8.33——enable-utf——enable-unicode-properties——enable-pcregrep-libz2——disable-static——disable-stack-for-递归
  5. 使
  6. sudo make install

在提供的指南中有一些额外的步骤,但我不必做它们。

更新:将可选元素设置为惰性(?)-> ??)是错误的,因为如果可能的话,它们将不包含在匹配的模式中。

最新更新