在 Linux 系统上使用终端操作文件的过程中,总会遇到在某个文件夹下查找文件的情况,如查找文件名为 add.py 的文件、查找文件30分钟前生成的文件、批量删除文件夹下(包含子文件)文件名中没有符号 ‘G’ 的文件,等等。这些都需要我们通过一个搜索命令来完成。本篇介绍 Linux 中的 find 命令,它能够帮我们完成上面的类似任务。本篇以 Ubuntu 18.04 为例进行演示。

find 命令语法

find 命令的基本语法如下:

1
find [路径] [参数] [命令]
  • find 的对象是文件夹,且可以同时指定多个文件夹路径;当不指定文件夹路径时,默认为当前路径;

  • find 参数部分可以配置很多,下面重点介绍。当不指定参数时,默认时打印 -print

  • 命令是可选参数,当需要直接对找到的文件或文件夹进行操作时,可以带上命令参数,一般命令参数都以 {} \; 结尾。

find 参数

find 参数是 find 强大功能的基础,通过配置不同的参数,可以使用 find 找到不同类型的文件或文件夹。

下面简单罗列一些常见的参数:

  • -type     只显示指定类型的文件或文件夹;

    1
    2
    3
    4
    5
    6
    7
    f    普通文件
    d 目录
    l 符号链接
    c 字符设备
    b 块设备
    s 套接字 socket
    p 具名贮列 Fifo
    1
    2
    3
    4
    # 只查找当前目录(包含子目录,本篇默认都包含子目录)下的文件
    find . -type f
    # 只查找当前目录下的子文件夹
    find . -type d
  • -name    文件名称符合 name 的文件;

    1
    2
    3
    4
    5
    6
    7
    # 查找当前目录中文件或文件夹名后面带 G 的
    find . -name "*G"
    # 查找当前目录下文件或文件夹后面不带 G 的
    find . ! -name "*G"
    find . -not -name "*G"
    # 统计代码行数, grep -v ^$ 表示排除空行
    find . -name "topology.py" | xargs cat | grep -v ^$ | wc -l
  • -iname 文件名称符合 name 的文件,但会忽略大小写;

    1
    2
    # 查找当前目录中文件或文件夹名后面带 G 或 g 的
    find . -name "*g"
  • -size 查找符合指定的文件大小的文件

    1
    2
    3
    4
    5
    6
    c    字节
    w 字(2字节)
    b 块(512字节)
    k 千字节(1024字节)
    M 兆字节(1024k字节)
    G 吉字节(1024M字节)
    1
    2
    3
    4
    5
    6
    # 查找大于10k的文件
    find . -type f -size +10k
    # 查找小于10M的文件
    find . -type f -size -10M
    # 查找等于1G的文件
    find . -type f -size 1G
  • -path 指定字符串作为寻找目录的范本样式;-ipath 忽略大小写;

    1
    2
    3
    4
    # 查找 /usr/ 目录下所有文件或文件夹路径中包含 'python' 的
    find /usr/ -path "*python"
    # 忽略大小写
    find /usr/ -ipath "*python"
  • -prune 排除指定字符串作为文件或文件夹的范本样式;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 在 /usr 目录下查找以 'py' 结尾的文件或文件夹,但排除文件夹 '/usr/local',即不在文件夹及其子文件夹下寻找 'py' 结尾的文件或文件夹
    find /usr -path "/usr/local" -prune -o -name "*py"

    # 删除文件夹结尾为 'G' 的
    # {} 用于与 -exec 选项结合使用来匹配所有文件夹,然后会被替换为相应的文件夹名
    find . -name "*G" -type d -prune -exec rm -rf {} \;
    # 此处如果不带 '-prune',则虽然能够删除成功,却会报如下错误,注意 106G 为当前目录下的一个符合条件的文件夹
    # find: ‘./106G’: No such file or directory
    # 或者采用如下命令,但是,需要把 -depth 放在 -type 之前
    find . -name "*G" -depth -type d -exec rm -rf {} \;

    # 删除 jupyterlab 运行时产生的 .ipynb_checkpoints 隐藏文件夹,避免程序运行出问题。 假设所有项目文件在当前用户目录下
    rm -rf `find ~ -type d -name .ipynb_checkpoints`
    # or
    find ~ -type d -name ".ipynb_checkpoints" -prune -exec rm -rf {} \;
    # or
    find ~ -depth -type d -name ".ipynb_checkpoints" -exec rm -rf {} \;

  • -atime 在过去 n 天内被读取过的文件或文件夹;-amin 在过去 n 分钟内被读取过的文件或文件夹;

  • -ctime 在过去 n 天内属性发生改变的文件或文件夹;-cmin 在过去 n 分钟内属性发生改变的文件或文件夹;

  • -mtime 在过去 n 天内内容发生改变的文件或文件夹;-mmin 在过去 n 分钟内内容发送改变的文件或文件夹;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 过去 1 天内修改过的文件
    find . -type f -mtime -1
    # 在过去 10 分钟内修改过的文件
    find . -type f -mmin -10
    # 在恰好 1 天前修改过的文件
    find . -type f -mtime 1
    # 在过去 1 天前修改过的文件
    find . -type f -mtime +1
    # 比文件 'test.log' 修改时间距离现在时刻更近的文件
    find . -type f -newer test.py
  • -delete 删除文件;

    1
    2
    # 删除以 '.txt' 结尾的文件
    find . -type f -name "*txt" -delete
  • -perm 文件权限;

    1
    2
    3
    4
    # 文件权限是 600 的文件
    find ~/.ssh -type f -perm 600
    # 文件其他组权限为 'r' 的文件
    find . -type f -perm -o=r
  • -user 文件所有者;

    1
    2
    # 文件所有者为 admin 的文件
    find . -type f -user admin
  • -group 文件所属组;

    1
    2
    # 文件所属组为 admin 的文件
    find . -type f -group admin
  • -path 目录;

    1
    2
    3
    4
    5
    6
    # 查找 / 目录下文件大小超过1G的文件,同时排除 /mnt 目录
    # 使用 ‘-path 目录路径 -prune -o’ 来排除指定的目录
    find / -path /mnt -prune -o -type f -size +1G

    # 如果有两个需要排除的目录
    find / -path /mnt -prune -o -path /root -prune -o -type f -size +1G
  • -exec 执行命令;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 切换文件所有者
    find . -type f -user root -exec chown jinzhongxu {} \;
    # 删除以 '.txt' 结尾的文件,并提示是否删除,输入 yes 后删除
    find . -name "*.txt" -type f -ok -exec rm {} \;
    # 把一周前的 '.log' 文件移动到 bak 文件夹中
    find . -name "*.log" -type f -mtime +7 -exec cp {} bak \;
    # 把所有 '.log' 结尾的文件以 “LOG:文件名” 的形式打印出来
    find . -name "*.log" -type f -exec printf "LOG: %s\n" {} \;
    # 如果需要执行的命令比较多,可以先把命令写入一个脚本中,然后像下面这样执行
    find . -name "*.log" -type f -exec ./js.sh {} \;
    # 删除 mac 下自动生成的文件
    find . -name '__MACOSX' -prune -exec rm -rf {} \;
  • -empty 文件内容为空的文件;

    1
    2
    find . -empty
    find . -type f -size 0 -exec ls -l {} \;
  • -depth 从指定的文件夹下最深层的子文件夹开始查找;

    1
    2
    # 从最深层子文件夹开始搜索
    find . -depth -mmin -300
  • -maxdepth 设置搜索的最大目录层级;

  • -mindepth 设置搜索的最小目录层级;

    1
    2
    3
    4
    # 只搜索当前文件夹第一层级的文件,即只搜索当前文件夹下的文件,而不搜索子文件夹
    find . -maxdepth 1 -mmin -300
    # 只搜索当前文件夹第二层级及更深层级的文件,即只搜索当前文件夹下的子文件夹及其更深子文件夹下的文件
    find . -mindepth 2 -mmin -300
  • 复杂运算式;

    复杂运算式,可以使用 ()开将运算式分隔,然后使用下面的逻辑运算:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #
    expr1 -and expr2

    #
    ! expr
    -not expr

    #
    expr1 -or expr2
    expr1, expr2

QA

  1. 使用命令时遇到权限不足会报错,影响判读:

    1
    2
    # 如下面的情况
    find: ‘/disk0/lost+found’: Permission denied

    此时,我们能够把这些错误的提示筛选丢弃掉即可。即在原来的命令后面增加2>/dev/null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 屏蔽所有错误信息
    find /disk0 -name "*ironman*.mp4" -type f -print 2> /dev/null
    # or
    find /disk0 -name "*ironman*.mp4" -type f -print 2>/dev/null
    # or
    find /disk0 -name "*ironman*.mp4" -type f 2>/dev/null

    # 只屏蔽权限错误信息。速度慢
    find /disk0/ -name "*ironman*.mp4" -type f 2>&1 | grep -v "Permission denied"
  2. 不想在文件前有 ./

    1
    find . -name "*.py"

    此时,可以使用 basename 来帮助:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    find . -name "*.py" -exec basename {} \;

    find $(pwd) -name "*.py"

    find . -name "*.py" -exec realpath {} \;
    find . -name "*.py" -exec readlink -f {} \;

    # 或者扩展名 .py 也不要
    find . -name "*.py" -exec basename {} .py \;

参考文献

  1. Linux find 命令 | 菜鸟教程

  2. find 命令,Linux find 命令详解:在指定目录下查找文件 - Linux 命令搜索引擎