Bash脚本-检查文件是否包含特定行

22

我需要检查一个文件是否包含特定的行。这个文件由某人持续写入,所以我将检查放在一个 while 循环中。

FILE="/Users/test/my.out"
STRING="MYNAME"
EXIT=1
while [ $EXIT -ne 0 ]; do 
    if [ -f $FILE ] ; then CHECK IF THE "STRING" IS IN THE FILE - IF YES echo "FOUND"; EXIT=0; fi
done

这个文件包含文本和多行内容。


1
你为什么不使用 grep(1) 呢? - Martin Tournoij
8个回答

24

如果$FILE包含文件名,$STRING包含待搜索的字符串,则可以使用以下命令显示文件是否匹配:

if $FILE 包含文件名和 $STRING 包含要搜索的字符串, 那么您可以使用以下命令来显示文件是否匹配:

if [ ! -z $(grep "$STRING" "$FILE") ]; then echo "FOUND"; fi

5
你应该引用你的变量:真实数据可能不仅仅是一个单词,文件名可能包含空格。无用的cat命令:grep可以接受文件名参数。 - glenn jackman
8
在我看来,这并没有真正回答问题,因为它也会报告“FOUND”,如果$STRING是行的一部分。问题是关于特定的存在。 grep "^$STRING"'$'在这里可以考虑,但不够通用,因为STRING可能包含某些元素,这些元素可能被误认为是正则表达式。这也适用于所提出的解决方案。 - Holger Brandl
2
当我在自己的bash脚本中使用它时,我一直收到“[: Status: binary operator expected”的错误提示。我将其更改为双方括号后,它就可以正常工作了。 - Juan Pablo
1
我想知道为什么这里没有人使用 fgrep (grep -F)... 这样,当搜索的字符串是文件行的子字符串时,这个检查会特别成功。 - x-yuri
由于某些原因,我在这里需要使用双重方括号[[https://dev59.com/DmAf5IYBdhLWcg3weyse#26712360。 - Code Slicer

11

以下是 grep 命令的手册:

-q, --quiet, --silent
    Quiet; do not write anything to standard output.  Exit immediately with zero status if any match is found, even if an error was detected.  Also see the -s or --no-messages option.
-F, --fixed-strings
    Interpret PATTERNS as fixed strings, not regular expressions.
-x, --line-regexp
    Select only those matches that exactly match the whole line.  For a regular expression pattern, this is like parenthesizing the pattern and then surrounding it with ^ and $.

# Inline
grep -q -F "$STRING" "$FILE" && echo 'Found' || echo 'Not Found'
grep -q -F "$STRING" "$FILE" || echo 'Not Found'

# Multiline
if grep -q -F "$STRING" "$FILE"; then
  echo 'Found'
else
  echo 'Not Found'
fi

if ! grep -q -F "$STRING" "$FILE"; then
  echo 'Not Found'
fi

如果需要检查整行,请使用-x标志:

grep -q -x -F "$STRING" "$FILE" && echo 'Found' || echo 'Not Found'

2
谢谢您。我对我的用例进行了快速性能检查,然后将其与“awk”答案进行了比较。原始运行时间:0.4秒;使用“awk”:0.3秒;使用此答案:0.09秒。 - Niet the Dark Absol

6

当文件修改时间改变时,轮询并搜索字符串:

while :; do
    a=$(stat -c%Y "$FILE") # GNU stat
    [ "$b" != "$a" ] && b="$a" && \
        grep -q "$STRING" "$FILE" && echo FOUND
    sleep 1
done

注意:BSD用户应使用stat -f%m

智能过滤mtime是个好主意。 - glenn jackman

4

如果要更严格地检查给定的 $STRING 是否包含在文件 FILE 中,可以使用以下方法:

awk '$0 == "'${STRING}'"' $FILE

这特别涉及到我对 @fstab 的回答的担忧(适用于所有其他之前的回答):它检查整个行,而不仅仅是行内子字符串的存在(如上面使用 grep 解决方案所做的那样)。

循环可以按照其他答案所示进行。


4

尝试:

while : ;do
    [[ -f "$FILE" ]] && grep -q "$STRING" "$FILE" && echo "FOUND" && break
done

这将不断循环而无需等待,您可能需要添加等待时间(例如,在此示例中为5秒):
while : ;do
    [[ -f "$FILE" ]] && grep -q "$STRING" "$FILE" && echo "FOUND" && break
    sleep 5
done

应该优先使用 grep -q:如果它找到了字符串,就不必读取整个文件:[[ -f "$f" ]] && grep -q "$str" "$f" && break - glenn jackman

0

此评论转化为答案,因为它似乎符合我对原问题的理解和期望:

cat <<EOF | grep -Fx "${search_term}"
hello
world
EOF
  • 搜索 helloworld 成功
  • 搜索 elloworl 失败

0

我需要在脚本中将ssh密钥添加到authorized_keys文件中。我找到了两个选项(好的和丑陋的:)):

for f in "$tmp/keys/"*; do
    line=`cat "$f"`
    dst_line=`fgrep "$line" "$home/.ssh/authorized_keys" || true`
    if [ "$line" != "$dst_line" ]; then
        echo "$line" >> "$home/.ssh/authorized_keys"
    fi
done

for f in "$tmp/keys/"*; do
    cat "$f" >> "$home/.ssh/authorized_keys"
done
cat "$home/.ssh/authorized_keys" | sort | uniq > "$home/.ssh/authorized_keys.new"
chmod 0600 "$home/.ssh/authorized_keys.new"
chown "$user:" "$home/.ssh/authorized_keys.new"
mv "$home"/.ssh/authorized_keys.new \
    "$home"/.ssh/authorized_keys

我以为用uniq会更简洁,但结果并不是这样。而且需要调整权限...还有行的顺序会改变...看起来不太好。


0
另一种解决方案是使用comm命令和-1以及-2开关:
cat <<EOF > file.txt
line1
line2
line3
EOF

line_in_file=line1
[[ -n "$(comm -12 <(sort <<< "$line_in_file") <(sort "file.txt"))" ]] && echo 'line1 in file'

line_not_in_file=line4
[[ -n "$(comm -12 <(sort <<< "$line_not_in_file") <(sort "file.txt"))" ]] && echo 'line4 in file'

这是man页的相关部分:
DESCRIPTION
       Compare sorted files FILE1 and FILE2 line by line.

       When FILE1 or FILE2 (not both) is -, read standard input.

       With no options, produce three-column output.  Column one contains lines unique to FILE1, column two contains lines unique to FILE2, and column three contains lines common to both files.

       -1     suppress column 1 (lines unique to FILE1)

       -2     suppress column 2 (lines unique to FILE2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接