Find 的基于成本的优化器打破了短路评估
Find's cost-based optimiser breaks short-circuit evaluation
引用 find 的手册页(GNU findutils 4.7.0,强调我的):
GNU find searches the directory tree rooted at each given starting-point by evaluating the given expression from left to right, according to the rules of precedence (see section PERATORS), until the outcome is known (the left hand side is false for and operations, true for or), at which point find moves on to the next file name.
因此,当 find
评估 <expr1> -and <expr2>
时,我希望 <expr2>
不会被评估,除非 <expr1>
为真,我依靠它来避免一些错误消息,特别是, 我不希望 find
测试不可读目录是否为空。这是一个 SCCCE:
mkdir some_dir
chmod 333 some_dir
find * -readable ! -empty -printf "yes" -or -printf "no" -prune
产生
find: ‘some_dir’: Permission denied
no
添加,否则隐式,-and
和括号,由 find
计算的表达式应该 等同于
( ( -readable -and (! -empty ) ) -and -printf "yes" ) -or ( -printf "no" -and -prune )
因此,在意识到some_directory
不可读之后,find
应该放弃对-printf "yes"
的空性测试和求值。相反,它应该跳转到 -printf "no"
的评估,最后是 -prune
。输出中的 "Permission denied" 表明它正在评估 -empty
。 (从原始表达式中删除 ! -empty
会使错误消失。)
使用-D tree
检查评估树,我看到优化后的形式(为简洁起见在此处编辑)是:
( ( ( ! -empty ) -and -readable ) -and -printf "yes" ) -or ( -printf "no" -and -prune )
据此 -empty
确实被评估,更糟糕的是,在 -readable
之前,这完全搞砸了预期的逻辑。我认为这是一个错误。 我说得对吗?
更新:(2020 年 5 月 26 日)已提交 bug report,开发人员已将其确认为错误。
在我看来,这是 findutils 的 "arm-swapping" 优化中的一个错误,因为它没有考虑到 -empty
和 -xtype
可能会导致 [=12] 的副作用=] 报告错误并以非零状态退出。我 reported 关于 -xtype
的相同问题,findutils 开发人员认为这是一个错误。这个 bug 也很难解决,因为 findutils 没有办法关闭这个优化。 -O0
相当于 -O1
已经应用了它。
如果您需要解决方法,我为 find
编写了一个名为 bfs
的替代品:https://github.com/tavianator/bfs。它完全兼容所有 GNU find 的选项,并且没有这个错误。
引用 find 的手册页(GNU findutils 4.7.0,强调我的):
GNU find searches the directory tree rooted at each given starting-point by evaluating the given expression from left to right, according to the rules of precedence (see section PERATORS), until the outcome is known (the left hand side is false for and operations, true for or), at which point find moves on to the next file name.
因此,当 find
评估 <expr1> -and <expr2>
时,我希望 <expr2>
不会被评估,除非 <expr1>
为真,我依靠它来避免一些错误消息,特别是, 我不希望 find
测试不可读目录是否为空。这是一个 SCCCE:
mkdir some_dir
chmod 333 some_dir
find * -readable ! -empty -printf "yes" -or -printf "no" -prune
产生
find: ‘some_dir’: Permission denied
no
添加,否则隐式,-and
和括号,由 find
计算的表达式应该 等同于
( ( -readable -and (! -empty ) ) -and -printf "yes" ) -or ( -printf "no" -and -prune )
因此,在意识到some_directory
不可读之后,find
应该放弃对-printf "yes"
的空性测试和求值。相反,它应该跳转到 -printf "no"
的评估,最后是 -prune
。输出中的 "Permission denied" 表明它正在评估 -empty
。 (从原始表达式中删除 ! -empty
会使错误消失。)
使用-D tree
检查评估树,我看到优化后的形式(为简洁起见在此处编辑)是:
( ( ( ! -empty ) -and -readable ) -and -printf "yes" ) -or ( -printf "no" -and -prune )
据此 -empty
确实被评估,更糟糕的是,在 -readable
之前,这完全搞砸了预期的逻辑。我认为这是一个错误。 我说得对吗?
更新:(2020 年 5 月 26 日)已提交 bug report,开发人员已将其确认为错误。
在我看来,这是 findutils 的 "arm-swapping" 优化中的一个错误,因为它没有考虑到 -empty
和 -xtype
可能会导致 [=12] 的副作用=] 报告错误并以非零状态退出。我 reported 关于 -xtype
的相同问题,findutils 开发人员认为这是一个错误。这个 bug 也很难解决,因为 findutils 没有办法关闭这个优化。 -O0
相当于 -O1
已经应用了它。
如果您需要解决方法,我为 find
编写了一个名为 bfs
的替代品:https://github.com/tavianator/bfs。它完全兼容所有 GNU find 的选项,并且没有这个错误。