【问题标题】:How to find files with three strings in a bash script?如何在 bash 脚本中查找包含三个字符串的文件?
【发布时间】:2020-01-07 21:45:35
【问题描述】:

要查找包含我使用的两个字符串的文件,

grep -l "$string1" `grep -l "$string2" /path/to/files/*.txt`

下面是示例输入的完整描述

脚本测试.sh

#!/bin/bash
string1="hello"
string2="good"
string3="world"
grep -l "$string1" `grep -l "$string2" /path/to/files/*.txt`

file1.txt

hi
good morning
everyone

file2.txt

hello everyone
good morning world
have a great day

file3.txt

hola
good day today
hello people
sunny morning 

运行脚本的输出:

/path/to/files/file2.txt
/path/to/files/file3.txt

【问题讨论】:

  • 您发布的脚本和您接受的答案都找不到包含 3 个字符串的文件。问题中甚至没有说明您对此类问题通常遇到的各种重要场景的要求是什么。如果您想要针对此问题的可靠解决方案,请发布一个新问题。

标签: bash


【解决方案1】:

根据你的使用情况,可以继续找三个这样的字符串。

grep -l '$string1' $(grep -l '$string2' `grep -l '$string3' /path/to/files/*.txt`)

【讨论】:

  • 大概$string1 是一个不会在单引号内展开的变量。您可能希望将反引号切换为 $(...) 以保持一致性和可读性。
【解决方案2】:

您的方法可以扩展到多个字符串,但您可能应该从反引号切换到现代 $(...) 命令替换语法。

grep -l "$string1" $(grep -l "$string2" $(grep -l "$string3" /path/to/files/*.txt))

(为了记录,历史反引号也可以嵌套,尽管它会变得丑陋;

grep -l "$string1" `grep -l "$string2" \`grep -l "$string3" /path/to/files/*.txt\``

但我不确定里面的引号是否会保留下来,你真的应该在上个千年停止使用这种语法。)

您也可以使用xargs 拆分这样的进程:

grep -l "$string1" /path/to/files/*.txt |
xargs grep -l "$string2" |
xargs grep -l "$string3"

但是,如果这些文件是大文件,则扫描 3 次效率非常低。您可以编写一个简单的 Awk 脚本来只扫描每个文件一次。

awk 'FNR==1 { s=t=u=0 }
    /string1/ { s=1 }
    /string2/ { t=1 }
    /string3/ { u=1 }
    s && t && u { print FILENAME; nextfile }' /path/to/files/*.txt

如果您的 Awk 真的很旧,它可能不支持 nextfile

逻辑应该是直截了当的;每个字符串的三个布尔值记录它是否已在此文件中看到。如果它们都是真的,我们就完成了这个文件并打印它的名字来表示成功。如果我们到达一个新文件(其中每个文件的行号 FNR 将被重置为 1)重新开始,所有布尔值都设置为零 (false)。

【讨论】:

  • 我是awk新手,慢慢学习。 FNR 在到达新文件时不会重置为 1 (stackoverflow.com/a/32482115/2923937) 吗?如果是,为什么要明确设置为 1?
  • @Perplexabot FNR==1 不是赋值,它是当这个表达式为真时触发的条件。注意双等号(单 = 是 Awk 中的赋值;双精度是比较)。
【解决方案3】:

使用find

find /path/to/files -type f -name '*.txt' \
     -exec grep -qF "$string1" {} \; \
     -exec grep -qF "$string2" {} \; \
     -exec grep -qF "$string3" {} \; \
     -print

请注意,这也会列出子文件夹中的匹配文件。为防止出现这种情况,您应该在/path/to/files 之后插入-maxdepth 1(即GNU 扩展),或使用此版本:

cd /path/to/files
find . ! \( -type d -path '*/*' -prune \) \
     -type f -name '*.txt' \
     -exec grep -qF "$string1" {} \; \
     -exec grep -qF "$string2" {} \; \
     -exec grep -qF "$string3" {} \; \
     -print

顺便说一句,您可以使用任意数量的字符串来完成这项工作。比如,假设您在一个名为file 的文件中有一百个字符串。首先,您需要将它们读入一个数组:

mapfile -t strs <file

然后,使用这个数组,你会为find 的参数生成另一个数组,并像这样使用它:

args=()
for str in "${strs[@]}"; do
  args+=('-exec' 'grep' '-qF' "$str" '{}' ';')
done

find /path/to/files -type f -name '*.txt' "${args[@]}" -print

【讨论】:

  • “一百个字符串”扩展看起来不错,尽管在此之前可能已经切换到我建议的 Awk 脚本。如果字符串是静态的,则将 -F 选项添加到 grep 是一个好主意,但当然,我们从问题中的信息中不知道这一点。
  • @triplee 感谢您的评论。不过,在这种情况下,您的 awk 脚本需要进行一些更改
  • 是的,当然。从文件中读取模式应该不难概括,但无论如何可能已经有一个重复的问题。这是一个好的开始:stackoverflow.com/questions/28896544/…
猜你喜欢
  • 2015-04-19
  • 1970-01-01
  • 1970-01-01
  • 2017-02-12
  • 2013-03-02
  • 2015-09-26
  • 2019-11-03
  • 2013-11-30
  • 1970-01-01
相关资源
最近更新 更多