find:为什么 -a 运算符与 -print 组合时不可交换?

find:为什么 -a 运算符与 -print 组合时不可交换?

假设我们在当前目录中使用 find 与音频文件(例如 MP3)和(重要!)当前目录或其下某处存在的非 mp3 文件(例如 JPEG)。
除此之外,我们还有一个目录,其中包含一些其他音频文件(称为排除我在示例中),它应该名副其实地被排除在文件搜索之外:

find . \( -type d -name 'exclude_me' -prune \) -o \( -type f -a -iname '*.mp3' -a -print \)

-a(尽管在本例中并非绝对必要,但为了清楚起见,我特意设置了一些多余的括号以及其他运算符。)

由于显式-print选项(也参见http://mywiki.wooledge.org/UsingFind),这确实会省略“exclude_me”目录中的任何内容,并且仅从当前目录递归地列出 *.mp3 文件。有时(并非总是)该alias命令对于单行代码来说可能已经足够了find,但该命令需要参数结尾的线。
请注意 -iname 的 '*.mp3' 参数是在这种-print情况下,似乎没有办法解决问题alias

但为什么不通过简单地交换两个选项来假装好像在数学中呢?
像这样:

find . \( -type d -name 'exclude_me' -prune \) -o \( -type f -a -print -a -iname '*.mp3' \)

请记住,我设置了一个前提条件,即 .mp3 文件中至少有一个非 mp3 文件(例如图像)。目录或其子目录。它已被设置为提供更清晰的证据来证明这一点不是在幕后按预期工作。没有他们,事情只会工作正常。拥有-iname '*.mp3'尽管单行代码中有不同的规范,但它们的存在反而会导致非 mp3 文件也被列出。那么为什么-a运算符不像数学中那样工作,其中“A + B”与“B + A”相同?或者,用find术语来说:

\( -type f -a -print -a -iname '*.mp3' \)

应该是一样的

\( -type f -a -iname '*.mp3' -a -print \)

或不?

有没有办法得到想要的结果-iname参数放在行尾,而不是中间的任何地方? (除了丑陋的黑客解决方案grep -v exclude_me之类的)

答案1

andor在许多编程语言中不可交换,例如 C、C++、C♯、shell、find 等等。

他们使用惰性从左到右评估,即首先评估左侧的术语,然后仅在需要时评估右侧的术语。因此,在 中false and bb不会被评估,因为答案总是错误的。但在b and falseb中被求值,因为它还没有发现第二项是假的。

当术语是纯函数(即谓词)(返回布尔值且不执行任何其他操作)时,这没有任何区别,但当它执行某些操作(有副作用)时,它就很重要。

-print做某事。它可以打印,因此很重要。

注意,任何允许副作用(例如 -print)的系统都必须定义计算顺序,从左到右可能是最简单的。

答案2

他们不一样。-print Primary 始终被评估为 true 并导致当前路径名被写入标准输出。

当您使用:

\( -type f -a -print -a -iname '*.mp3' \)

所有找到的文件都会打印到标准输出,这是-print 的默认行为,无论-iname '*.mp3'表达式是真还是假。

当您使用:

\( -type f -a -iname '*.mp3' -a -print \)

-print-iname '*.mp3'仅当表达式为真时才到达部分。因此,只有以 结尾的文件.mp3才会被打印。

POSIX 定义查找-a 运算符作为:

表达式 [-a] 表达式

初选的结合; AND 运算符由两个原色的并置隐含或通过可选的 -a 运算符明确表示。如果第一个表达式为 false,则不应计算第二个表达式。

在这种情况下,您不必使用-print,因为如果表达式为 true,-print则默认添加:

如果不存在表达式,则应使用 -print 作为表达式。否则,如果给定表达式不包含任何主元 -exec、-ok 或 -print,则给定表达式应有效地替换为:

(给定的表达式)-打印

但这会导致排除我打印在输出中,因为当find匹配它时,-prune会导致表达式评估为 true。由于我们-print在第二个表达式中省略了 ,因此-print将其添加到两个表达式的末尾。看起来像:

find . \( -type d -name 'exclude_me' -prune \) -print -o \( -type f -a -iname '*.mp3' \) -print

这导致排除我被打印。

你总是可以省略排除我通过显式添加第二-print个表达式在输出中。这导致-print仅适用于第二个表达式:

find . \( -type d -name 'exclude_me' -prune \) -o \( -type f -a -iname '*.mp3' \) -print

或者在 后面添加一个表达式-prune,并确保它使第一个表达式为 false:

find . \( -type d -name 'exclude_me' -prune -a -name . \) -o \( -type f -a -iname '*.mp3' \)

答案3

使用 shell 函数而不是别名:

function my_find
{
   exclude_dir="$1"
   shift
   find . \( -type d -name "$exclude_dir" -prune \) -o \( -type f -a -iname "$@" -a -print \)
}

相关内容