我在 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