find命令，由不能打印‘afile’总结到的
Tags: find, xargs, afile
Published: 2024-05-28 09:30:54

## 关于
find命令是常用到的，读到manpage里面一段话不是很明白，于是整个manpage读了一遍终于整明白了。
```
Please note that -a when specified implicitly (for example by two
tests appearing without an explicit operator between them) or
explicitly has higher precedence than -o.  This means that `find .
-name afile -o -name bfile -print` will never print afile.
```
就是说`find . -name afile -o -name bfile -print`命令即使afile存在也不会打印出来，这就很奇怪了，读起来是afile或者bfile就print，看起来很直观，但是不是这么判断的，下面我们总结下manpage和常见用法习惯。

## 用法
```bash
find [OPTIONS] [starting-point] [EXPRESSION]
```
- OPTIONS 里面 `-H -L -P` 用于表示如何处理symbolic link; `-O` 表示优化级别，一般用不到; `-D` 用于调试，比如
```bash
`-D help` 打印出所有调试选项
`-D tree` 打印出predicate list，可以看到默认添加的参数等，非常有用
```
- starting-point 可以有多个，最好使用相对路径和绝对路径，比如`./abc`和`/abc`, 避免被乱解释了，比如路径开头为`-`，就会被解释为expression argument。
- `EXPREESION` 用于表示怎么匹配文件和匹配后怎么做。包括以下几个部分： `Tests`, `Actions`, `Global options`, `Positional options`, `Operators`。
     1. `Tests`  
     返回`true`或者`false`。根据文件属性包括日期、权限、状态、大小等，选择我们关心部分作为测试参数，比如`-empty`表示文件内容为空的判断
     2. `Actions`  
     返回`true`或者`false`。 匹配文件怎么做，比如，`-print`将当前匹配文件名打印
     3. `Global Options`   
     总是返回`true`。 影响所有的tests和actions参数，比如, `-depth`指示find按照深度优先方式traverse
     4. `Positional Options`   
     总是返回`true`。 只是影响他之后的tests和actions参数，比如，`-regextype` 指示他后面使用的regex方言类型
     5. `Operators`   
     逻辑连接参数，如果expr之间没有逻辑连接默认是`-a`表示and。 **而且不管显式或者隐式`-a`优先级都高于`-o`**，其他有趣的还包括  
     `()` 调整precedence   
     `! expr`或者`(-not expr)` 取反    
     `expr1, expr2` 2个表达式都会执行，但是结果取expr2的, 举例
     ```bash
     find . ! -path './node_modules/*' \( \( -type f -print \) , \( -type d -exec echo 'directory: {}' \; \) \)     
     ``` 

## 注意点
1. 默认action是`-print`, 这里的默认是说，如果命令中没有任何一个action, 会在最后添加`-a -print`, 但是如果有多个判断分支，但一个有action, 那不会添加默认，比如
     ```bash
     find . -name afile -o -name bfile -print
     ```
     这个afile判断是不会添加`-print`的，由于`-a`优先级高于`-o`, 所以最后变成了
     ```bash
     find . ( -name afile ) -o ( -name bfile -a -print )
     ```
     其中默认添加了`-a`, 遇到afile时候就会跳过了，相当于continue, 可以使用`-D tree`看到最后执行的命令表达式
     `find -D tree . -name afile -o -name bfile -print`会得到：
     ```bash
     Optimized Command Line:
          -name afile [est success rate 0.1] -o [est success rate 0.2]  ( -name bfile [est success rate 0.1] -a [est success rate 0.1] -print [est success rate 1]  )
     ```

2. 如果只有`-prune`或者`-quit`，还是会添加默认`-print`。     
     `-prune`表示如果是文件夹，不递归他。`-prune`总是返回true, 常用于跳过某个文件或者目录，比如`find . -path './node_modules' -prune -o -type f -print`;   
     `-quit`用于成功了整个命令退出，比如打印第一个匹配成功的文件 `find . -path './node_moduels' -prune -o -type f -print -quit`。

3. 以下actions会禁止`-print`, 包括`-delete, -exec, -execdir, -ok, -okdir, -fls, -fprint, -fprintf, -ls, -print, -printf`。
4. `-maxdepth 0` (global options)可以指示只处理start point
5. 为了防止被当成SHELL中特殊字符，find命令中参数有些要注意转译，比如括号、分号、加号等，凡是在SHELL中特殊字符尽量转移，不然后果未知, 比如`find . -path './node_modules' -prune -o -type f -exec echo {} ;`如果最后的`;`不转译，find命令找不到结束符号报错。还有最后的`+`也是一样, `find . -path './node_modules' -prune -o -type f -ok echo {} \;`, `-ok`每次执行命令都会询问。
6. `-exec COMMAND [\;|\+]` 
     最后`;`和`+`, 前者是每次都执行一次COMMAND, 后者是将参数以空格形式分割，最后执行一次COMMAND。
7. `-name pattern`
     表示file的**basename**是否符合pattern, 比较时候会去掉leading directory
8. `-path pattern`
     匹配的是整个filename, 包括路径, 比如
     ```bash
     find . \( ! -path './node_modules/*' ! -name 'eslint.config.js' -type f \) -print
     ```     
9. `-print0`  
    主要是防止文件名含有特殊字符，比如`\n`, `space`等，批处理时会被转译截断，所以使用`\0`结尾，跟`xargs -0`配对使用
    
## 答案
`-a`优先级比`-o`高，另外`-name afile`不会添加`-print`, 所以即使匹配到了还是会无声的过了，最后只是打印了bfile, 如果存在的话。