我正在使用find
和grep
命令。
对于何时使用“或”与标志连接多个选项-o
以及使用分组括号以及何时不使用分组括号感到非常困惑。
使用时find
,分组括号似乎是必要的
find $fdir ( -name *.texi -o -name *.org )
使用时grep
,不使用分组括号
grep --include "*.texi" --exclude "*.org"
答案1
大多数程序,包括grep
不专门将括号视为参数。如果你这样做:
grep "(" --include "*.texi" --exclude "*.org" ")"
grep
会将第一个(
视为要搜索的模式,最后一个)
视为文件名。(*)foo
就好像它们是一样bar
。因此,您无法将选项分组到grep
.
但事情是这样的:-name
、-type
、-o
等(
不是选项到find
。它确实需要一些选项,即-P
/ -H
/ -L
,这会影响符号链接处理,但这些不是选项。相反,它们是搜索表达式的一部分,这是特定于find
. (**)
强调表达那里。当你给出find
表达式时( -name *.texi -o -name *.org )
,它更像是类似 C 的表达式
( patternmatch(filename, "*.texi") || patternmatch(filename, "*.texi") )
比其他任何事情都重要。并find
为它看到的每个文件评估该表达式。如果你有例如这个:
( -name *.texi -o -name *.org ) -printf something
你需要括号,因为没有它们:
-name *.texi -o -name *.org -printf something
会是一样的
-name *.texi -o -name *.org -a -printf something
因为有一个隐含的和除非给出原子之间的关系-o
,否则表达式将是
patternmatch(...) || patternmatch(...) && printf(...)
和和操作比或者运算,与几乎所有编程语言中的运算方式完全相同,并且乘法比加法绑定得更紧密。并且find
无法知道你想要什么,因为它支持任意表达式。(***)所以,在这种情况下,如果没有括号,它就不会像你想要的那样工作。
正如其他人指出的,您所拥有的命令不需要括号,因为如果查找表达式中没有“操作”(-print
等-exec
),它默认会打印匹配的文件名,并且还会隐式在表达式两边加上括号。
所以,
find "$fdir" -name "*.texi" -o -name "*.org"
行为就像
find "$fdir" \( -name "*.texi" -o -name "*.org" \) -print
但如果您显式地将 放在-print
那里,则还需要显式地放置括号以获得正确的处理顺序。看:具有多个“-name”和“-exec”的“find”仅执行“-name”的最后匹配项
回到grep
:grep
不带括号,也不需要它们,因为它不处理表达式。它没有嵌套或运算符的概念,例如和和或者一般来说。相反,它具有硬编码的行为。对于--include
和--exclude
,我认为它试图同时满足包含和排除规则。 (或者,至少有一个单独的--include
规则,但没有一个单独的--exclude
规则。)但是对于多种搜索模式,匹配一个就足够了,或者其他。这两个都是静态规则:您无法为其提供更复杂的表达式来说明哪些模式应该匹配。
(* GNU grep 会将中间的作为选项,其他实现也可能将它们作为文件名,因为非选项参数之前停止了选项处理。此外,您需要引用或转义括号以防止它们对壳;这与他们所做的事情无关grep
。)
(** 同样,它特定于grep
第一个非选项参数是模式,只有其余参数是文件名,或者最后一个参数是mv
目标,而其他参数是要移动的文件,并且它特定于git
这些工具会执行不同的操作,因此它们必须以不同的方式使用命令行参数。)
(*** 有人曾经说过,求值表达式是主要的事 find
做。也就是说,它没有找到要打印的文件名,而是通过文件树来查找计算表达式在他们。打印和运行外部命令只是一个副作用。)
答案2
答案3
造成混乱的原因是程序如何解释命令行参数没有标准。一般来说,参数的解释留给程序员,尽管GNU编码标准建议程序使用getopt()
和getopt_long()
函数(来自GNU C 库) 为了这个目的。
这意味着定义运算符优先级的括号的解释是 的函数find
,而不是用于调用 的 shell 的函数find
。 “简单”的程序员grep
没有以这种方式实现他们的选项解析算法,所以grep
一开始就不会理解这个符号。
但请注意括号在 shell 中具有特殊含义:它们表示所包含的内容是要运行的命令在子外壳中。因此,您发布的命令实际上应该不起作用;正如 @terdon 所提到的,您必须转义括号(如\( ... \)
)才能让 shellfind
首先将它们传递给它们。
答案4
我认为该手册对如何find
使用括号有一些见解可以帮助您:
运算符根据测试和操作构建复杂的表达式。运算符按优先级降序排列:
( expr )
强制优先。如果
expr
为真则为真。! expr -not expr
如果为假则为真
expr
。在某些 shell 中,有必要保护 '!'通过引用它来从 shell 解释中获得。expr1 expr2 expr1 -a expr2 expr1 -and expr2
和;如果为 false,
expr2
则不进行评估。expr1
expr1 -o expr2 expr1 -or expr2
或者;如果为 true,
expr2
则不进行评估。expr1
expr1 , expr2
列表;两者
expr1
和expr2
总是被评估。如果expr2
为真则为真。的值expr1
被丢弃。该运算符允许您在一次遍历中执行多个独立操作,而不依赖于其他操作是否成功。这两个操作expr1
并不expr2
总是完全独立的,因为expr1
可能会产生副作用,例如触摸或删除文件,或者可能使用-prune’ which would also affect
expr2`。
find
根据优先级规则,通过从左到右评估表达式来搜索以每个文件名为根的目录树,直到知道结果(左侧对于 为假-and
,对于 为真-or
),此时 find 继续进行下一个文件名。