在Linux中查找包含关键字的文件并列出所在目录

5
在我的目录层次结构中,有许多名为STATUS.txt的文本文件。这些文本文件每个都包含一个关键字,例如COMPLETE、WAITING、FUTURE或OPEN。我希望执行以下形式的shell命令:./mycommand OPEN它会列出所有包含名为STATUS.txt的文件夹,其中此文件夹包含文本"OPEN"。
将来,我需要扩展此脚本,以便返回的目录已排序。排序由存储在与STATUS.txt相同的目录中的数字值PRIORITY.txt决定。然而,这可以等到我的能力提高之后再处理。目前,我愿意按任何顺序列出目录。
我已经在Stack Overflow上搜索了以下内容,但没有找到合适的答案:
  • unix filter by file contents
  • linux filter by file contents
  • shell traverse directory file contents
  • bash traverse directory file contents
  • shell traverse directory find
  • bash traverse directory find
  • linux file contents directory
  • unix file contents directory
  • linux find name contents
  • unix find name contents
  • shell read file show directory
  • bash read file show directory
  • bash directory search
  • shell directory search
我尝试过以下shell命令: 这能帮助我识别所有包含STATUS.txt的目录
$ find ./ -name STATUS.txt

这会读取每个包含 STATUS.txt 文件的目录

$ find ./ -name STATUS.txt | xargs -I{} cat {}

这并没有返回任何文字,我原本希望它会返回每个目录的名称

$ find . -type d | while read d; do if [ -f STATUS.txt ]; then echo "${d}"; fi; done
6个回答

3
也许你可以尝试这个:
grep -rl "OPEN" . --include='STATUS.txt'| sed 's/STATUS.txt//'

其中grep -r表示递归,-l表示仅列出匹配的文件,'.'是目录位置。您可以将其传输到sed中以删除文件名。

然后,您可以将其包装在bash脚本文件中,您可以将关键字(例如“OPEN”,“FUTURE”)作为参数传递。

#!/bin/bash
grep -rl "$1" . --include='STATUS.txt'| sed 's/STATUS.txt//'

1
你应该加上--include="STATUS.txt",否则如果其他文件名中包含了OPEN,它仍会返回这些文件名。 - Reinstate Monica Please
非常感谢您的迅速回复。我在使用这个命令时没有遇到任何好运气。它似乎卡住了。 - Insert name here
更正:它会挂起几分钟,然后返回一个包含许多错误结果的巨大列表。我认为这可能就是BroSlow在--include="STATUS.txt"中所提到的问题。 - Insert name here
@Insertnamehere,是的,你需要通过文件名来限制它,至少GNU grep可以这样做,不确定是否有方法只使用BSD grep来实现。就其在打印许多结果之前花费很长时间而言,这似乎是缓冲区问题。你可以尝试使用“--line-buffered”,尽管这将在技术上使总运行时间略微延长。 - Reinstate Monica Please
@BroSlow 感谢您指出 --include 参数,我已经更新了答案。 - Wan B.
不必使用 sed 技巧,您可以使用 grep -h 避免打印文件名。此外,请注意您正在使用的 sed 带有 /g,这是不必要的,因为您只想让它在每行中执行一次,而 g 会使其出现与 STATUS.txt 相同的次数。 - fedorqui

3

...或者反过来:

find . -name "STATUS.txt" -exec grep -lF "OPEN" \{} +

如果您想将其包装在脚本中,一个很好的起点可能是:

如果您想将其包装在脚本中,一个很好的起点可能是:

#!/bin/sh

[ $# -ne 1 ] && echo "One argument required" >&2 && exit 2
find . -name "STATUS.txt" -exec grep -lF "$1" \{} +

正如 @BroSlow 指出的那样,如果你正在寻找包含匹配的STATUS.txt文件的目录,这可能更符合你的需求:
fgrep --include='STATUS.txt' -rl 'OPEN' | xargs -L 1 dirname 

或者更好。
fgrep --include='STATUS.txt' -rl 'OPEN' |
           sed -e 's|^[^/]*$|./&|' -e 's|/[^/]*$||'
#              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#            simulate `xargs -L 1 dirname` using `sed`  
#      (no trailing `\`; returns `.` for path without dir part)                   

1
我会添加grep选项-l,以仅输出文件路径名。 - glenn jackman
1
“-f” 应该是什么意思?在GNU和BSD的“grep”中,它是否从文件中读取模式? - Reinstate Monica Please
据我所理解,OP需要搜索一个“固定字符串”,而不是一个“模式”。由于他显然想将其包装在脚本中,所以我认为强调这一点是个好主意。我的错误是使用小写的f而不是大写的F。愚蠢的打字错误...感谢您注意到它! - Sylvain Leroux
1
@SylvainLeroux 我认为他也只想要目录名称,而不是实际的文件名。否则,你可以像这样做:grep --include='STATUS.txt' -rl 'OPEN' . - Reinstate Monica Please
@BroSlow 很好的发现。我没有看到标签“Linux” - 而是在寻找一些“标准”的东西。fgrep --include='STATUS.txt' -rl 'OPEN' | xargs -L 1 dirname 怎么样? - Sylvain Leroux
感谢您的快速回复。命令fgrep --include='STATUS.txt' -rl 'OPEN' | sed -e 's|^[^/]*$|./&|' -e 's|/[^/]*$||'确实完成了任务。 - Insert name here

1
尝试类似于这样的东西。
find -type f -name "STATUS.txt" -exec grep -q "OPEN" {} \; -exec dirname {} \;

或者在脚本中
#!/bin/bash 
(($#==1)) || { echo "Usage: $0 <pattern>" && exit 1; }
find -type f -name "STATUS.txt" -exec grep -q "$1" {} \; -exec dirname {} \;

谢谢您的迅速回复。这正是我想要的。我发现这种方法最易读,并且看起来比其他人建议的方法执行更快。 - Insert name here

0

0
你可以使用 grepawk 代替 find 命令:
grep -r OPEN * | awk '{split($1, path, ":"); print path[1]}' | xargs -I{} dirname {}

以上的grep将递归地列出包含“OPEN”的所有文件,其目录结构类似于以下内容:
dir_1/subdir_1/STATUS.txt:OPEN
dir_2/subdir_2/STATUS.txt:OPEN
dir_2/subdir_3/STATUS.txt:OPEN

然后,awk脚本将在冒号处分割此输出,并打印其第一部分(目录路径)。

dir_1/subdir_1/STATUS.txt
dir_2/subdir_2/STATUS.txt
dir_2/subdir_3/STATUS.txt

dirname 会返回文件所在目录的路径,不包括文件名,我想这才是你想要的。

如果你想进一步发展,我建议考虑使用 Perl 或 Python ,因为如果你想添加优先级和排序功能,使用其他语言可能会变得更加混乱。


我认为使用Perl或Python是一个很好的建议。我接下来会研究一下这个。我本来希望一个shell脚本能够处理所有事情 - 我喜欢使用尽可能少的软件包。 - Insert name here
那就用Perl吧。我使用过的几乎所有*nix系统都默认安装了Perl。 - lsdr

-1

我认为你应该编写一个Python脚本,它可以:

  • 检查你的目录结构并找到所有名为STATUS.txt的文件。
  • 对于每个找到的文件:
    • 读取文件并根据文件内容执行mycommand

如果您想以后扩展脚本以进行排序,您可以先找到所有有趣的文件,将它们保存到列表中,对列表进行排序,然后在排序后的列表上执行命令。

提示:http://pythonadventures.wordpress.com/2011/03/26/traversing-a-directory-recursively/


澄清一下,“mycommand”旨在在基本级目录下执行一次。然而,递归地在每个包含特定关键字的STATUS.txt目录中执行某些命令的方法 - 并使用脚本语言(如Python)来实现这一点 - 是我考虑未来扩展的内容。感谢您提供的网站链接。 - Insert name here

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