Featured image of post Linux find 命令从入门到实战:循序渐进训练计划

Linux find 命令从入门到实战:循序渐进训练计划

title: Linux find 命令从入门到实战:循序渐进训练计划 slug: 1790409995201 date: 2026-09-26 summary: 从按名称查找到定时日志归档脚本,一份循序渐进的 find 命令训练计划,附 3 个可直接使用的完整运维脚本。 tags:

  • Linux
  • find
  • Shell
  • 运维

find 是 Linux 下最强大的文件查找工具之一,但它的参数多、组合灵活,很多初学者望而却步。这篇文章给你一份循序渐进的训练计划:从基础查找到 3 个真实运维场景的完整脚本,跟着练一遍,find 就真正变成你的肌肉记忆了。

准备工作:搭建练习环境

动手之前,先创建一个测试目录,造一些文件用来练手:

1
2
3
4
5
mkdir -p ~/find_test && cd ~/find_test
touch file1.txt file2.log .hidden_file script.sh large_file.bin
mkdir -p sub1/sub2
touch sub1/a.txt sub1/sub2/b.log
chmod 777 script.sh

建议每学一个命令,都在这个目录里实际跑一遍,看输出是否符合预期。

第一阶段:按名称与类型查找

题目 1:按名称查找 .log 文件

查找当前目录下所有以 .log 结尾的文件:

1
find . -name "*.log"

如果想不区分大小写(比如同时匹配 .LOG),用 -iname:

1
find . -iname "*.log"

题目 2:只查找目录

1
find . -type d

-type 是最常用的过滤条件之一:f 表示普通文件,d 表示目录,l 表示符号链接。

题目 3:查找隐藏文件

Linux 下以 . 开头的是隐藏文件:

1
find . -name ".*"

第二阶段:按大小与时间查找

题目 4:查找大于 10MB 的文件

1
find . -size +10M

-size 的单位很有讲究:+10M 表示大于 10MB,-10M 表示小于 10MB,10M 表示约等于。单位还可以用 k(KB)、G(GB)、c(字节)。

题目 5:查找 24 小时内修改过的文件

1
2
3
find . -mtime -1
# 或者用分钟粒度:
find . -mmin -1440

-mtime -1 表示修改时间在 1 天以内,-mmin -1440 是同样的意思(24×60=1440 分钟)。

题目 6:查找 7 天前被访问过的文件

1
find . -atime +7

注意这里是 +7(7 天以前),不是 -7。atime 是访问时间,mtime 是修改时间,ctime 是状态改变时间——三者的区别是面试常客,值得记住。

第三阶段:深度、权限与逻辑组合

题目 7:限制查找深度

只在当前目录下找 .txt 文件,最多深入两层子目录:

1
find . -maxdepth 2 -name "*.txt"

-maxdepth 建议放在其他条件前面,否则在某些 find 实现里会给出警告。

题目 8:在 /var/log 下不区分大小写找日志

1
find /var/log -iname "*.log"

题目 9:按权限查找

查找权限为 777 的文件(安全排查常用):

1
sudo find /var -perm 777 -ls

-perm 777 是精确匹配;-perm -777 表示"至少包含"这些权限位;-perm /777 表示"包含其中任意"。排查可疑文件时 -perm -4000(找 SUID 文件)也很常用。

题目 10:逻辑或组合

查找 .jpg 或 .png 图片:

1
find /data \( -name "*.jpg" -o -name "*.png" \)

括号需要转义(或加引号),否则 shell 会误解。-o 是 OR,-a 是 AND(默认可省略),! 是 NOT。

题目 11:排除 .git 目录

查找所有 .sh 脚本,但跳过 .git:

1
find . -path "*/.git" -prune -o -name "*.sh" -print

这行值得拆开理解,它本质是个 if-else:

  • -path "*/.git":当前路径是不是 .git 目录?
  • -prune:如果是,直接剪掉这根"树枝",不往下递归
  • -o:否则,执行后面的逻辑
  • -name "*.sh" -print:匹配 .sh 并打印

第四阶段:用 -exec 处理查找结果

题目 12:交互式删除 30 天前的临时文件

1
find /tmp -name "*.tmp" -mtime +30 -ok rm {} \;

-ok 和 -exec 一样执行命令,但每个文件都会先问你确认。批量删除前先用 -ok,或者先不加删除动作 dry-run 跑一遍,是保命的好习惯。

题目 13:批量修改目录权限为 755

1
find /var/www/html -type d -exec chmod 755 {} \;

{} 是查找到的文件的占位符,\; 表示命令结束。如果文件很多,用 + 代替 \; 可以把多个文件拼成一条命令执行,效率更高:

1
find /var/www/html -type d -exec chmod 755 {} +

题目 14:复制小配置文件到备份目录

1
2
mkdir -p /backup/config
find . -name "*.conf" -size -10k -exec cp {} /backup/config/ \;

注意提前建好目标目录,否则 cp 会报错。

综合实战:3 个完整脚本

前面都是单条命令,下面是 3 个真实运维/开发场景的完整脚本。需求来自实战,脚本逐行写好并加了注释,可以直接拿去用。

实战 1:定时日志清理与归档脚本

场景:服务器日志占满磁盘,需要定期归档旧日志并清理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/bin/bash
#
# archive_old_logs.sh - 日志归档清理脚本
# 功能:查找指定目录下超过 7 天未修改的 .log 文件,
#       打包为 old_logs_<日期>.tar.gz 存到 /tmp/backup/,验证无误后删除原文件
# 用法:./archive_old_logs.sh /var/log/app
# 建议:配合 crontab 每天凌晨执行
#   0 2 * * * /path/to/archive_old_logs.sh /var/log/app >> /var/log/archive.log 2>&1

set -euo pipefail

# ---------- 1. 参数校验 ----------
LOG_DIR="${1:-}"
if [ -z "$LOG_DIR" ]; then
    echo "用法: $0 <日志目录>"
    echo "示例: $0 /var/log/app"
    exit 1
fi

if [ ! -d "$LOG_DIR" ]; then
    echo "错误: 目录 '$LOG_DIR' 不存在"
    exit 1
fi

BACKUP_DIR="/tmp/backup"
DATE_STR=$(date +%Y%m%d)
ARCHIVE_PATH="${BACKUP_DIR}/old_logs_${DATE_STR}.tar.gz"
mkdir -p "$BACKUP_DIR"

# ---------- 2. 查找旧日志 ----------
echo "正在扫描 $LOG_DIR ..."
mapfile -t OLD_FILES < <(find "$LOG_DIR" -type f -name "*.log" -mtime +7 -print)

if [ ${#OLD_FILES[@]} -eq 0 ]; then
    echo "没有发现超过 7 天未修改的 .log 文件,无需处理"
    exit 0
fi

echo "发现 ${#OLD_FILES[@]} 个旧日志文件:"
printf '  %s\n' "${OLD_FILES[@]}"

# ---------- 3. 打包归档 ----------
echo "正在打包到 $ARCHIVE_PATH ..."
# -print0 + --null 可正确处理带空格/特殊字符的文件名
find "$LOG_DIR" -type f -name "*.log" -mtime +7 -print0 \
    | tar --null -czf "$ARCHIVE_PATH" --files-from=- --no-recursion

if [ ! -s "$ARCHIVE_PATH" ]; then
    echo "错误: 打包失败,保留原文件"
    exit 1
fi
echo "打包成功: $ARCHIVE_PATH ($(du -h "$ARCHIVE_PATH" | cut -f1))"

# ---------- 4. 校验归档完整性,通过后再删除 ----------
echo "正在校验归档文件..."
if tar -tzf "$ARCHIVE_PATH" > /dev/null 2>&1; then
    echo "校验通过,删除原文件..."
    find "$LOG_DIR" -type f -name "*.log" -mtime +7 -delete
    echo "清理完成,共删除 ${#OLD_FILES[@]} 个文件"
else
    echo "错误: 归档文件损坏,保留原文件以防数据丢失"
    exit 1
fi

设计要点:

  • 先打包、校验通过后再删除,任何一步失败都不会丢数据
  • 用 -print0 + tar --null 处理文件名中的空格和特殊字符
  • set -euo pipefail 让脚本遇到错误立即退出,避免误删文件

实战 2:代码库大文件与敏感信息排查脚本

场景:提交代码前,检查是否误带了大文件或私钥。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/bin/bash
#
# precommit_check.sh - 提交前检查脚本
# 功能:扫描项目目录(忽略 .git),找出大于 10MB 的文件和敏感私钥文件
# 用法:在项目根目录执行 ./precommit_check.sh [目录]
# 建议:做成 git pre-commit hook,提交时自动检查

set -euo pipefail

SCAN_DIR="${1:-.}"
SIZE_LIMIT_MB=10
FOUND_ISSUE=0

echo "正在扫描: $SCAN_DIR"
echo "======================================"

# ---------- 1. 大文件检查 ----------
echo "[1/2] 检查大文件 (>${SIZE_LIMIT_MB}MB)..."
mapfile -t BIG_FILES < <(find "$SCAN_DIR" -path "*/.git" -prune -o -type f -size +${SIZE_LIMIT_MB}M -print)

if [ ${#BIG_FILES[@]} -eq 0 ]; then
    echo "  ✓ 未发现大文件"
else
    FOUND_ISSUE=1
    echo "  ⚠ 发现 ${#BIG_FILES[@]} 个大文件:"
    for f in "${BIG_FILES[@]}"; do
        SIZE_MB=$(du -m "$f" | cut -f1)
        echo "    ${SIZE_MB}MB  $f"
    done
fi

echo ""

# ---------- 2. 敏感文件检查 ----------
echo "[2/2] 检查敏感文件 (.key / .pem / id_rsa)..."
mapfile -t SENSITIVE < <(find "$SCAN_DIR" -path "*/.git" -prune -o -type f \
    \( -name "*.key" -o -name "*.pem" -o -name "*id_rsa*" \) -print)

if [ ${#SENSITIVE[@]} -eq 0 ]; then
    echo "  ✓ 未发现敏感文件"
else
    FOUND_ISSUE=1
    echo "  ⚠ 警告!发现 ${#SENSITIVE[@]} 个疑似敏感文件:"
    printf '    %s\n' "${SENSITIVE[@]}"
    echo "  请确认这些文件不在 git 追踪范围内!"
fi

echo "======================================"
if [ $FOUND_ISSUE -eq 0 ]; then
    echo "检查通过,可以安全提交"
    exit 0
else
    echo "发现问题,请处理后再提交"
    exit 1
fi

设计要点:

  • 用 -path "*/.git" -prune 跳过 git 内部目录,避免误报
  • 非零退出码可以直接接入 git hook,拦截问题提交
  • 还可以扩展:检查 .env、*password*、*.p12 等

实战 3:Web 目录权限批量安全修复脚本

场景:Web 服务器安全加固,规范文件/目录权限与所有者。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash
#
# fix_web_perm.sh - Web 目录权限修复脚本
# 功能:目录设为 755,文件设为 644,所有者修正为 Web 用户
# 用法:sudo ./fix_web_perm.sh /var/www/wordpress [web_user]

set -euo pipefail

TARGET_DIR="${1:-}"
WEB_USER="${2:-www-data}"

# ---------- 1. 参数与环境检查 ----------
if [ -z "$TARGET_DIR" ]; then
    echo "用法: $0 <目标目录> [Web用户]"
    echo "示例: $0 /var/www/wordpress www-data"
    exit 1
fi

if [ ! -d "$TARGET_DIR" ]; then
    echo "错误: 目录 '$TARGET_DIR' 不存在"
    exit 1
fi

if [ "$(id -u)" -ne 0 ]; then
    echo "警告: 修改文件所有者需要 root 权限,建议用 sudo 运行"
fi

echo "目标目录: $TARGET_DIR"
echo "Web 用户: $WEB_USER"
echo "======================================"

# ---------- 2. 目录权限 755 ----------
echo "[1/3] 设置目录权限为 755..."
DIR_COUNT=$(find "$TARGET_DIR" -type d | wc -l)
find "$TARGET_DIR" -type d -exec chmod 755 {} +
echo "  已处理 $DIR_COUNT 个目录"

# ---------- 3. 文件权限 644 ----------
echo "[2/3] 设置文件权限为 644..."
FILE_COUNT=$(find "$TARGET_DIR" -type f | wc -l)
find "$TARGET_DIR" -type f -exec chmod 644 {} +
echo "  已处理 $FILE_COUNT 个文件"

# ---------- 4. 修正所有者 ----------
echo "[3/3] 修正所有者为 ${WEB_USER}:${WEB_USER}..."
if id "$WEB_USER" &>/dev/null; then
    WRONG_COUNT=$(find "$TARGET_DIR" ! -user "$WEB_USER" | wc -l)
    if [ "$WRONG_COUNT" -gt 0 ]; then
        find "$TARGET_DIR" ! -user "$WEB_USER" -exec chown "${WEB_USER}:${WEB_USER}" {} +
        echo "  已修正 $WRONG_COUNT 个文件的所有者"
    else
        echo "  所有者均正确,无需修改"
    fi
else
    echo "  警告: 用户 '$WEB_USER' 不存在,跳过所有者修正"
fi

echo "======================================"
echo "权限修复完成"

设计要点:

  • 用 {} + 代替 {} \;,把多个文件合并为少量 chmod/chown 调用,大目录下快得多
  • ! -user 只处理所有者不对的文件,避免无意义的 chown
  • 注意:644/755 是通用安全基线,上传目录等特殊场景可能需要微调

练习建议

  1. 先抄后改:把上面的命令在 ~/find_test 里亲手跑一遍,观察输出
  2. 故意犯错:试试去掉 -print、写错 -mtime 的正负号,看看会发生什么——犯错是最好的老师
  3. 改造脚本:把实战 1 的 7 天改成 30 天,把实战 2 接入你项目的 pre-commit hook
  4. 查手册:man find 是终极文档,重点看 -perm 的三种写法和 -exec 的 + vs \;

find 的强大在于组合:条件是"筛子",-exec 是"手"。筛子越细、手越稳,你在 Linux 下就越游刃有余。


本文由 BOSH 的博客助手 HerMes 整理 🔍

热爱生活 学无止境
使用 Hugo 构建
主题 Stack 由 Jimmy 设计