Raj*_*ath 17 command-line grep find
我有一个abc/def/efg包含许多子目录(例如:)的目录(例如abc/def/efg/(1..300))。所有这些子目录都有一个公共文件(例如,file.txt)。我只想在这个file.txt排除其他文件的情况下搜索一个字符串。我怎样才能做到这一点?
我用过 grep -arin "pattern" *,但是如果我们有很多子目录和文件,它会很慢。
Zan*_*nna 24
在父目录中,您可以使用find然后grep仅在这些文件上运行:
find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +
Run Code Online (Sandbox Code Playgroud)
Eli*_*gan 24
像 Zanna 的回答一样grepfind,使用构建命令是一种高度健壮、通用且可移植的方法(另请参阅sudodus 的回答)。而穆鲁发布了使用的一个非常好的方法grep的--include选择。但是如果你只想使用grep命令和你的 shell,还有另一种方法 -你可以让shell 本身执行必要的递归:
shopt -s globstar # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txtRun Code Online (Sandbox Code Playgroud)
即使只找到一个匹配的文件,该-H标志grep也会显示文件名。如果您需要-a,您也可以将-i, 和-n标志(来自您的示例)传递给grep。但不要通过-r或-R使用此方法时。是shell在扩展包含 的 glob 模式时递归目录**,而不是grep.
这些说明特定于 Bash shell。Bash 是 Ubuntu(以及大多数其他 GNU/Linux 操作系统)中的默认用户 shell,因此如果您使用 Ubuntu 并且不知道您的 shell 是什么,那么几乎可以肯定它是 Bash。尽管流行的 shell 通常支持目录遍历**glob,但它们的工作方式并不总是相同。有关更多信息,请参阅Stéphane Chazelas对The result of ls * , ls ** 和 ls *** on Unix.SE的出色回答。
打开globstar bash shell 选项会**匹配包含目录分隔符 ( /) 的路径。因此,它是一个目录递归全局。具体来说,如man bash解释:
当启用globstar shell 选项并在路径名扩展上下文中使用 * 时,用作单个模式的两个相邻 * 将匹配所有文件以及零个或多个目录和子目录。如果后跟 /,则两个相邻的 *s 将仅匹配目录和子目录。
你应该小心这一点,因为你可以运行修改或删除比你预期更多的文件的命令,特别是**当你打算写*. (在这个命令中是安全的,它不会改变任何文件。)shopt -u globstar关闭 globstar shell 选项。
find.find远比 globstar 多才多艺。你可以用 globstar 做的任何事情,你也可以用find命令做。我喜欢globstar,有时它更方便,但globstar不是一般的替代find。
上面的方法不会查看名称以.. 有时您不想递归此类文件夹,但有时您会这样做。
与普通的 glob 一样,shell 会构建所有匹配路径的列表,并将它们作为参数传递给您的命令 ( grep),而不是 glob 本身。如果调用file.txt的文件太多,导致生成的命令太长,系统无法执行,则上述方法将失败。实际上,您需要(至少)数千个这样的文件,但它可能会发生。
使用的方法find不受此限制,因为:
Zanna 的方式构建并运行一个grep可能包含许多路径参数的命令。但是,如果找到的文件多于单个路径中可以列出的文件数,则+-terminated-exec操作会使用某些路径运行命令,然后使用更多路径再次运行该命令,依此类推。在对grep多个文件中的字符串进行ing的情况下,这会产生正确的行为。
就像这里介绍的 globstar 方法一样,这会打印所有匹配的行,并在每个行前加上路径。
sudodus 的方式grep为每个file.txt找到的单独运行。如果有很多文件,它可能比其他一些方法慢,但它有效。
find使用 globstar 的直接好处之一是,默认情况下在 Ubuntu 上,grep将产生彩色输出。但是,您可以轻松地得到这个find,太。
Ubuntu 中的用户帐户是使用别名创建的,该别名使grep真正运行grep --color=auto(运行alias grep以查看)。这是一件好事,是别名几乎只有当你发给他们交互式扩展,但它意味着,如果你想find调用grep与--color标志,你就必须把它明确写入。例如:
find . -name file.txt -exec grep --color=auto -H 'pattern' {} +Run Code Online (Sandbox Code Playgroud)
mur*_*uru 19
你不需要find这个;grep可以自己很好地处理这个问题:
grep "pattern" . -airn --include="file.txt"
Run Code Online (Sandbox Code Playgroud)
来自man grep:
--exclude=GLOB
Skip files whose base name matches GLOB (using wildcard
matching). A file-name glob can use *, ?, and [...] as
wildcards, and \ to quote a wildcard or backslash character
literally.
--exclude-from=FILE
Skip files whose base name matches any of the file-name globs
read from FILE (using wildcard matching as described under
--exclude).
--exclude-dir=DIR
Exclude directories matching the pattern DIR from recursive
searches.
--include=GLOB
Search only files whose base name matches GLOB (using wildcard
matching as described under --exclude).
Run Code Online (Sandbox Code Playgroud)
muru's answer 中给出的方法,grep使用--include标志运行以指定文件名,通常是最好的选择。但是,这也可以通过find.
此答案中的方法用于为找到的每个文件单独find运行grep,并在每个文件中找到的匹配行上方恰好打印每个文件的路径一次。(在其他答案中介绍了在每个匹配行前面打印路径的方法。)
您可以将目录更改为包含这些文件的目录树的顶部。然后运行:
find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;
Run Code Online (Sandbox Code Playgroud)
这会打印.每个名为 的文件的路径(相对于当前目录,包括文件名本身)file.txt,后跟文件中所有匹配的行。这是有效的,因为它{}是找到的文件的占位符。每个文件的路径都通过以 为前缀与其内容分开设置#####,并且在该文件的匹配行之前仅打印一次。(file.txt不包含匹配项的调用文件仍会打印其路径。)您可能会发现此输出比从在每个匹配行的开头打印路径的方法获得的输出更混乱。
find像这样使用几乎总是比grep在每个文件 ( grep -arin "pattern" *)上运行更快,因为find搜索具有正确名称的文件并跳过所有其他文件。
Ubuntu 使用 GNU find,即使出现在更大的字符串中,它也总是会扩展{},比如##### {}:. 如果您需要命令来工作,find对系统可能不支持这种,或者你更喜欢使用-exec只有在绝对必要的行动,你可以使用:
find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;
Run Code Online (Sandbox Code Playgroud)
为了使输出更易于阅读,您可以使用 ANSI 转义序列来获取彩色文件名。这使得每个文件的路径标题从它下面打印的匹配行中更加突出:
find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
Run Code Online (Sandbox Code Playgroud)
这会导致您的 shell将绿色的转义码转换为在终端中产生绿色的实际转义序列,并对正常颜色的转义码执行相同的操作。这些转义被传递给find,它在打印文件名时使用它们。($' '此处需要引用,因为find的-printf操作无法识别\e解释 ANSI 转义码。)
如果你愿意,你也可以使用-exec与系统的printf命令(其中不支持\e)。所以做同样事情的另一种方法是:
find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
Run Code Online (Sandbox Code Playgroud)