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

use*_*834 6 command-line bash grep text-processing

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

string="

test1

test2

"
Run Code Online (Sandbox Code Playgroud)

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

this is a test:
test1

test2
and another one
Run Code Online (Sandbox Code Playgroud)

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

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


以下不起作用:

string="
    
    test1
    
    test2
    
    "
if ! grep -q string "test.txt"; then
    echo "$string" >> test.txt
fi
Run Code Online (Sandbox Code Playgroud)

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


编辑:

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

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

"  
Run Code Online (Sandbox Code Playgroud)

ter*_*don 6

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

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

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

$ string="1
> 2"

$ seq 10 | grep "$string"
1
2
10
"
Run Code Online (Sandbox Code Playgroud)

这意味着恐怕您必须将模式表示为正确的正则表达式:

\n\ntest1\n\ntest2\n\n
Run Code Online (Sandbox Code Playgroud)

然而,这也意味着您需要该-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
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

将所有这些放在一起可以得出:

string='\n\ntest1\n\ntest2\n\n'
if ! grep -Pzq "$string" test.txt; then
    printf "$string" >> test.txt
fi
Run Code Online (Sandbox Code Playgroud)

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

$ string="

    test1

    test2

    "
$ if ! grep -Pzq "${string//$'\n'/\\n}" test.txt; then
    printf "$string" >> test.txt
fi
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)