驾驭unix find 输出的n种方式

find 命令是 Linux/Unix 系统管理员和开发者工具箱中的瑞士军刀,它能帮助我们根据各种条件定位文件.一旦找到了这些文件,我们通常需要对它们执行某些操作——删除、复制、修改权限,或者更复杂的操作.之前我一直使用 xargs 来将 find 的输出作为参数传递给其他命令,但是今天发现xargs处理复杂操作的时候有点麻烦,就尝试了其他方法.

xargs

xargs [options] [command [initial-arguments]]

本质上是替换从标准输入读取数据(用空格或者换行分割),然后将这些数据作为参数传递给另一个命令来执行(默认是echo)

xargs 通过将大量输入参数“打包”分批传递给目标命令,从而显著减少目标命令的调用次数.这样做避免了为每个输入项都启动一个新进程的巨大开销,因此极大地提高了处理大量输入的性能.此外,它还提供了如 -n, -L 选项来控制打包方式,以及 -P 选项来支持并行处理,进一步优化效率.

由于unix文件名可能带有空格或者换行,所以-0option,并给unix程序(如find)传递-print0来用空字符作为分割符,因为unix哲学的(让每个程序都成为一个过滤器 (Make every program a filter))

例子

1
find . -name "*.log" -print0 | xargs -0 rm

更复杂的例子(openwrt)更新所有软件包

1
opkg list-upgradable | awk '{print $1}' | xargs opkg upgrade

实现细节

什么叫做”The command line for command is built up until it reaches a system-defined limit”

对于:

1
ls | xargs

可以用strace看看他调用了多少次echo

1
2
3
ls | strace -f -e trace=execve xargs 2>&1

ls | strace -f -e trace=execve xargs 2>&1 | grep 'execve(".*/echo"'

多线程处理

可以用 -P来控制线程数量

1
2
3
4
5
6
7
find . -maxdepth 1 -type f -name '*.png' -print0 | xargs -0 -P "$num_cores" -I {} sh -c '
input_file="$1"
output_basename=$(basename "$input_file" .png) # 移除 .png 后缀
output_file="compressed_images/${output_basename}.jpg"
echo "处理: $input_file -> $output_file"
magick "$input_file" -quality 90% -format jpg "$output_file"
' sh {}

如果想传递全局变量进去:

1
2
3
4
5
6
7
8
find . -type f -name '*.jpg' -print0 | xargs -0 -P "$num_cores" -I {} sh -c '
input_file="$1"
target_width="$2"
backup_dir="$3"

.....
' _ {} "$target_width" "$backup_dir"

find自带的exec

删除所有找到的 .log 文件 (每个文件执行一次 rm)

1
find . -name "*.log" -exec rm {} \

高效删除所有找到的 .log 文件(类似与xargs)

1
find . -name "*.log" -exec rm {} +

还有execdir(在目录下执行)

while read 循环

1
2
3
find . -name "*.log" -print0 | while IFS= read -r -d $'\0' file; do
rm "$file"
done
  • -print0: find 使用 null 字符 (\0) 作为文件名分隔符.
  • IFS=:清空内部字段分隔符,防止 read 意外处理文件名前后的空白.
  • read -r: 禁止反斜杠转义.
  • read -d $'\0': read 以 null 字符作为记录分隔符.

可读性高(复杂操作不用像xargs全部写近''里面(没语法高亮QAQ))但是这样的性能不如xargs

选择?

  • 对于大多数标准的批量文件操作,find ... -exec command {} + 是首选,因为它兼具效率、安全性和简洁性.
  • 如果需要在 shell 脚本中对每个文件执行更复杂的逻辑,find ... -print0 | while IFS= read -r -d $'\0' file; do ... done 是最安全和最灵活的选择.
  • 当你确实需要 xargs 提供的特定功能(如并行处理 -P)时,确保使用 find -print0 | xargs -0 ... 来保证文件名处理的安全性.

驾驭unix find 输出的n种方式
https://20040702.xyz/2025/05/25/shell-xargs/
作者
Seeker
发布于
2025年5月25日
许可协议