我有一个abc/def/efg
包含许多子目录(例如:)的目录(例如,abc/def/efg/(1..300)
)。所有这些子目录都有一个公共文件(例如file.txt
)。我只想在此字符串中搜索file.txt
其他文件。我怎样才能做到这一点?
我曾经用过 grep -arin "pattern" *
,但是如果我们有很多子目录和文件,那将非常慢。
我有一个abc/def/efg
包含许多子目录(例如:)的目录(例如,abc/def/efg/(1..300)
)。所有这些子目录都有一个公共文件(例如file.txt
)。我只想在此字符串中搜索file.txt
其他文件。我怎样才能做到这一点?
我曾经用过 grep -arin "pattern" *
,但是如果我们有很多子目录和文件,那将非常慢。
Answers:
就像Zanna的答案中那样,grep
使用构建命令find
是一种高度健壮,多功能且可移植的方式(请参见sudodus的答案)。而且,muru发布了使用grep
的--include
选项的出色方法。但是,如果只想使用grep
命令和外壳程序,则还有另一种方法可以使外壳程序本身执行必要的递归:
shopt -s globstar # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt
即使仅找到一个匹配文件,该-H
标志grep
也会显示文件名。如果需要的话-a
,也可以将-i
,和-n
标志(来自示例)传递给grep
。但是不要通过-r
或-R
使用此方法时。是外壳程序在扩展包含**
而不是grep
的glob模式时递归目录。
这些说明特定于Bash shell。Bash是Ubuntu(以及大多数其他GNU / Linux操作系统)中的默认用户外壳程序,因此,如果您在Ubuntu上并且不知道外壳程序是什么,则几乎可以肯定是Bash。尽管流行的shell通常支持目录遍历**
glob,但是它们并不总是以相同的方式工作。欲了解更多信息,请参阅斯特凡Chazelas的出色答卷,以LS的结果*,LS **和*** LS上Unix.SE。
启用globstar bash shell选项将使**
匹配路径包含目录分隔符(/
)。因此,它是目录递归的glob。具体来说,如下所述man bash
:
当globstar壳选项被启用,并且*是在路径扩展上下文中使用的,两个相邻* S用作单一模式将匹配的所有文件和零个或多个目录和子目录。如果后跟一个/,则两个相邻的* 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
多个文件中输入一个字符串的情况下,这将产生正确的行为。
就像这里介绍的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' {} +
bash
外壳程序才能正常工作。您确实在“ globstar bash shell选项”中暗含了它,但是阅读速度太快的人们很容易错过它。
**
glob,但您的核心批判是正确的:**
此答案中的呈现特定于bash,shopt仅是bash,术语 “ globstar”是(我认为)bash和仅tcsh。我本来是因为这些复杂性而忽略了这一点,但是您是对的,这有点令人困惑。我没有在这个答案中进行详尽的讨论,而是链接到另一篇(很详尽的)文章,它很繁重。
-e
不应该将其应用于路径,但这很容易解决。对于第一个命令,只需省略-e
。对于第二个,使用find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
或find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
。用户有时会偏爱您的方式(-e
固定用法),而另一种方式是每条匹配行打印一条路径;您的文件会在找到的每个文件中打印一个路径,然后显示grep
结果。
grep
它本身不会做你在做什么。其他一些批评也是错误的。如果没有(或)grep -H
,-exec
则不会变色。IEEE 1003.1-2008不能保证可以扩展,但是Ubuntu可以找到GNU查找。如果您还可以,我将编辑您的帖子以修复该错误(并澄清其用例),您可以查看是否要删除。(我的代表可以查看/编辑已删除的帖子。)--color
GREP_COLOR
{}
##### {}:
-e
您不需要find
这个;grep
可以自行处理完美:
grep "pattern" . -airn --include="file.txt"
来自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).
find?
在muru的答案中给出的方法(使用grep
带有--include
指定文件名的标志运行)通常是最佳选择。但是,也可以使用完成此操作find
。
此答案中的方法用于为找到的每个文件单独find
运行grep
,并在每个文件中找到的匹配行上方,仅一次打印每个文件的路径。(其他答案中涵盖了在每条匹配行的前面打印路径的方法。)
您可以将目录更改为拥有这些文件的目录树的顶部。然后运行:
find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;
该命令将打印.
每个名为的文件的路径(相对于当前目录,包括文件名本身)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" {} \;
为了使输出更易于阅读,可以使用ANSI转义序列来获取彩色文件名。这使得每个文件的路径标题与在其下打印的匹配行相比更加突出:
find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
这会导致你的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" {} \;
find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
cd abc/def/efg
'change directory'命令:-)
-e
选项echo
?这将导致它处理任何包含反斜杠的文件名。(2)不能保证将其{}
用作参数的一部分。说-exec echo "#####" {} \;
还是会更好-exec printf "##### %s:\n" {} \;
。(3)为什么不只是使用-print
或-printf
?(4)也考虑grep -H
。
find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;
2)您可能是对的,但到目前为止,这对我来说是可行的。3)-print和-printf也是替代方案。4)这已经在主要答案中了。-无论如何,我们欢迎您回答:-)
-exec
电话。只需使用grep -H
,即可打印文件名(彩色)以及匹配的文本。
只是指出,如果可以将问题的条件应用于文学,则可以使用直接grep:
grep 'pattern' abc/def/efg/*/file.txt
要么
grep 'pattern' abc/def/efg/{1..300}/file.txt