使用find,grep,sed在bash脚本中进行分段错误

时间:2013-05-31 11:31:39

标签: bash sed grep segmentation-fault

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

这是脚本正在做的事情:

  1. 搜索不包含字符串X
  2. 的文件
  3. 在这些文件中,搜索包含字符串Y
  4. 的文件
  5. 使用for-loop
  6. 迭代返回的文件列表
  7. 如果文件内容与模式A匹配,则将模式A替换为A_TAG
  8. 模式B,C,D(文件只能包含A,B,C,D中的一个)相同
  9. 模式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导致缓冲区溢出,但我还没有办法确定或解决这个问题。

2 个答案:

答案 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.htmlhttp://www.linuxfromscratch.org/blfs/view/svn/general/pcre.html

  1. http://downloads.sourceforge.net/pcre/pcre-8.33.tar.bz2
  2. 下载pcre
  3. tar jxf pre-8.33.tar.bz2
  4. cd pcre-8.33
  5. ./ configure --prefix = / usr --docdir = / usr / share / doc / pcre-8.33 --enable-utf --enable-unicode-properties --enable-pcregrep-libz2 --disable-static --disable堆栈换递归
  6. 使
  7. sudo make install
  8. 提供的指南中有一些额外的步骤,但我没有必要这样做。

    更新:使可选元素变得懒惰(? - > ??)是一个错误,因为如果可能,它们将不会包含在匹配的模式中。