如何使用 grep 检查文件中带有换行符的特定字符串?

如何使用 grep 检查文件中带有换行符的特定字符串?

我在 bash 脚本文件中有一个字符串变量,如下所示:

string="

test1

test2

"

我想检查一个文件是否test.txt包含这个特定的字符串(包括换行符。例如,如果它只包含以下内容,它就会失败:

this is a test:
test1

test2
and another one

因为 test1 上方和 test2 下方的换行符不存在。

(我之所以要检查这个是因为我想检查某段代码是否存在于源文件中,如果不在,就添加它。)


以下不起作用:

string="
    
    test1
    
    test2
    
    "
if ! grep -q string "test.txt"; then
    echo "$string" >> test.txt
fi

这正确地将字符串添加到文件中,但即使字符串已经添加,它也会这样做。此外,当我将字符串更改为没有换行符时,它也能正确执行。


编辑:

下面的@terdon 和@steeldriver 的答案适用于我上面写的字符串示例,但由于某种原因,它们不适用于这个更现实的示例:

string="                                                                
                                                               
if [ -f ~/.script ]; then                            
        . ~/.script         
fi

"  

答案1

问题是,它将grep在每一行上运行,而不是整个文件。只要文件足够小,可以放入内存(现在绝大多数情况下都是如此),你就可以使用 grep 的-z标志来读取整个文件:

-z, --null-data 将输入和输出数据视为行序列,每行以零字节(ASCII NUL 字符)而不是换行符结尾。与 -Z 或 --null 选项一样,此选项可与 sort -z 等命令一起使用来处理任意文件名。

下一个问题是,如果你传递grep带有换行符的内容,它会将其视为要 grep 的模式列表:

$ string="1
> 2"

$ seq 10 | grep "$string"
1
2
10
"

这意味着恐怕你必须将模式表达为适当的正则表达式:

\n\ntest1\n\ntest2\n\n

但是,这也意味着您需要该-P标志来启用与 perl 兼容的正则表达式,以便其\n能够正常工作。

我创建了这两个文件来演示:

$ cat file1
this is a test:
test1

test2
and another one

$ cat file2
this is a test:

test1

test2

and another one

使用这两个文件和上述信息,您可以执行以下操作:

$ grep -Pz '\n\ntest1\n\ntest2\n\n' file1
$ 

$ grep -Pz '\n\ntest1\n\ntest2\n\n' file2
this is a test:

test1

test2

and another one

综合以上所有,我们可以得出:

string='\n\ntest1\n\ntest2\n\n'
if ! grep -Pzq "$string" test.txt; then
    printf "$string" >> test.txt
fi

或者,正如@steeldriver 在评论中所建议的那样,您可以使用变量并\n动态地将换行符转换为:

$ string="

    test1

    test2

    "
$ if ! grep -Pzq "${string//$'\n'/\\n}" test.txt; then
    printf "$string" >> test.txt
fi

如果您的字符串包含在正则表达式中有意义的特殊字符,正如您在更新的问题中所示,那么情况就完全不同了。对于您展示的示例,您需要一些更复杂的东西。像这样:

searchString='\n\nif \[ -f ~/.script \]; then\s*\n\s*\.\s+~/\.script\s*\nfi\n\n'
printString='
if [ -f ~/.script ]; then
   . ~/.script         
fi

'
if ! grep -Pzq "$searchString" test.txt; then     
    printf "%s" "$printString" >> test.txt 
fi

答案2

您可能需要考虑使用pcregrep-M选项--multiline来允许匹配文字换行符:

   -M, --multiline
             Allow patterns to match more than one line. When this  option
             is given, patterns may usefully contain literal newline char‐
             acters and internal occurrences of ^ and  $  characters.

例如给定

$ cat test.txt
this is a test:
test1

test2
and another one


    test1

    test2
    
    

$ cat test2.txt
this is a test:
test1

test2
and another one


    test3

    test4
    
    

$ string="

    test1

    test2

    "

然后

$ pcregrep -qM "$string" test.txt && echo 'found' || echo 'not found'
found

$ pcregrep -qM "$string" test2.txt && echo 'found' || echo 'not found'
not found

答案3

使用 awk 在文件中搜索多行模式可能会更容易:

awk '/Start pattern/,/End pattern/' filename

查看这个帖子更多详细信息

相关内容