当前位置:首页>Linux>每天学一个Linux命令系列(5):rm - 删库跑路前,这些安全措施你做了吗

每天学一个Linux命令系列(5):rm - 删库跑路前,这些安全措施你做了吗

  • 2026-09-11 05:07:40
每天学一个Linux命令系列(5):rm - 删库跑路前,这些安全措施你做了吗
运
运维少年 · 科技观察

这个命令是干啥的

rm,Remove,删除文件或目录。Linux 里最危险也最让人又爱又恨的命令,没有之一。

我记得刚做运维那会儿,带我的师傅说最经典的一句话就是:rm -rf / 能让你从运维变成"前运维"。

后来我自己也踩过坑。有一次想在 /var/log/nginx 下清空日志,本来想打 rm -rf *.log,结果手滑多打了个空格成了 rm -rf * .log。. 前面的 * 展开成了所有文件,差点把整个 /var/log 目录的东西都删了。

还有一次同事在跑脚本的时候,脚本里写的是 rm -rf ${DIR}/。结果 $DIR 变量因为某些原因没赋值,变成了 rm -rf /。你们懂的。

从那以后,我对 rm 就充满了敬畏心。安全措施一个都不能少。

基本用法(3分钟上手)

# 删除一个文件
rm file.txt

# 删除多个文件
rm file1.txt file2.txt file3.txt

# 删除目录,-r是recursive递归
rm -r dir_name

# 强制删除,不加-f会让你确认
rm -rf dir_name

# 删除空目录(非空会报错)
rm -d empty_dir

-rf 组合是我敲得最多的,但也是最危险的。每次敲 rm -rf 之前,我都强制自己停一秒,看一眼路径对不对。

删之前确认一下:

# 交互模式,删之前问一句
rm -i file.txt

# 超过3个文件才问
rm -I file1 file2 file3 file4

-i 适合逐个确认的场景,但如果文件很多的话,问几千次你也会烦。-I 是 "少啰嗦" 版,删除超过 3 个文件或者递归删除时问你一次。我一般用 -I,比 -i 干脆,但比什么都没有强。

查看要删什么:

# 用ls先确认一下
ls -la *.log

# 确认没问题再删
rm -f *.log

这条黄金法则记住了:删除前先用 ls 看一眼。有时候你觉得 * 匹配的是 10 个文件,结果其实是 100 个。看一眼心里有数。

进阶骚操作

用 trash-cli 代替 rm:

这个是我最推荐的。把 rm 变成"移到回收站"而不是"真的删掉"。

# 安装trash-cli
sudo apt install trash-cli    # Debian/Ubuntu
sudo yum install trash-cli    # CentOS(可能没有,要装EPEL)

# 使用
trash file.txt
trash dir/

# 列出回收站
trash-list

# 还原文件
trash-restore

# 清空回收站
trash-empty

# 删除7天前的回收站内容
trash-empty 7

我所有服务器上都装了 trash-cli 并且做了别名。

# 在.bashrc或.zshrc里
alias rm='trash'
# 这样每次敲rm,实际上是trash命令
# 但注意:sudo rm 不会走别名,该危险还是危险

不过有些系统的 trash-cli 会把文件移到 ~/.local/share/Trash/,如果文件很大,磁盘很快就满了。所以得搭配 trash-empty 定期清理。

通配符陷阱深度解析:

通配符展开是 shell 干的,不是 rm 的事。shell 先把 * 展开成文件名列表,再把列表传给 rm。

# 你想删所有.txt文件
rm *.txt
# 但如果没有.txt文件,shell展开成空
# 实际上变成了 rm 加上空参数
# rm会报错,不会删东西。但风险在下面...

# 更危险的
rm -rf * .txt
# 注意中间有空格!这其实是两个参数
# * 展开成所有文件,.txt作为第二个文件
# 相当于"删所有文件再加上一个叫.txt的文件"

用 echo 预览展开结果:

# 看看*到底展开成什么
echo *.log

# 或者用ls确认
ls -la *.log

执行 rm 之前先用 echo 或 ls 看看 * 展开了什么,这个习惯能救你一命。

安全的 rm 写法:

# 在脚本里,先cd到目录再删
cd /target/dir && rm -rf ./some_dir

# 用相对路径比绝对路径安全
rm -rf ./logs   # 只删当前目录下的logs
rm -rf /logs    # 可能会删根目录下的logs

# 路径后不加斜杠,防止变量为空时变成 /
rm -rf "${DIR}"dir_name
# 而不是
rm -rf "${DIR}/dir_name"

/bin/rm vs /usr/bin/rm:

# 查看rm的位置
which rm
# 通常输出 /bin/rm 或 /usr/bin/rm

# 两个文件可能是硬链接或软链接
ls -li /bin/rm /usr/bin/rm

在一些系统里 /bin 是指向 /usr/bin 的软链接。但个别系统有自己的 rm 实现。这些差异在写跨平台脚本的时候要注意。

# 如果你在脚本里需要保证用系统的rm
/bin/rm -rf /some/dir
# 这样可以绕过别名

避坑指南

坑1:rm -rf / 真的能删一切

有些系统有保护,会提示 "rm: it is dangerous to operate recursively on /"。但不是所有系统都有,别指望这个。

# 保护措施
# 在 / 下创建一个叫 -no-preserve-root 的文件?NO!
# 正确方式
alias rm='rm -I --preserve-root'

# --preserve-root 参数让rm拒绝递归删除 /
alias rm='rm -i'

不过这些别名在 sudo 下面是不生效的。sudo rm -rf / 照样删。

坑2:变量为空导致灾难

DIR=
# $DIR没赋值
rm -rf /data/$DIR/logs
# 实际执行: rm -rf /data/logs   (还行)

# 但如果
rm -rf /data/${DIR}/
# 实际执行: rm -rf /data/
# 因为 ${DIR} 为空,变成了 rm -rf /data/

解决方案:判断变量是否为空再执行。

# 安全写法
[ -n "$DIR" ] && rm -rf "/data/$DIR/logs"

# 或者设置默认值
rm -rf "/data/${DIR:?变量为空,拒绝执行}/logs"

# 或者用&&串联
cd "/data/${DIR}" && rm -rf ./*

坑3:rm 删不掉特殊字符文件名的文件

# 文件名是 -r 或 --help 怎么办
touch -- -r
rm -r    # 惨了,rm以为你是让它递归删除

# 正确做法
rm -- -r
# -- 告诉rm后面不是参数了

# 或者用inode删
ls -li
# 找到inode号
find . -inum 123456 -delete

坑4:rm 删不掉"只读"文件系统

# 报了 Read-only file system
rm -f /some/file

# 检查挂载
mount | grep /some

# 重新挂载为读写
mount -o remount,rw /some

遇到这种问题别急着加 -f,先看看文件系统状态。

坑5:误删后别再写磁盘

# 误删了文件
# 1. 马上停止所有写操作
# 2. 用 debugfs 或 extundelete 尝试恢复
# 3. 越早恢复成功率越高

# 如果是文本文件,还在内存里的话
# 看一下有没有进程还在占用
lsof | grep deleted

不过说实话,删掉的文件能不能找回来看运气。所以最好的补救措施是备份。

实战场景(重点!结合真实运维场景)

场景一:清理过期日志

公司的日志轮转策略没配好,日志文件越攒越多,磁盘报警了。

# 安全删除30天前的日志
LOG_DIR="/var/log/myapp"
DAYS=30

# 先用find查看要删什么
find "$LOG_DIR" -name "*.log" -mtime +$DAYS -ls

# 确认无误再删
find "$LOG_DIR" -name "*.log" -mtime +$DAYS -delete

# 或者用rm配合find
find "$LOG_DIR" -name "*.log" -mtime +$DAYS -exec rm -f {} \;

find -delete 比 -exec rm 更快更安全。我一般在删之前一定先跑一遍 -ls 确认。

场景二:发布脚本中的安全删除

每次发版前要清理旧文件:

#!/bin/bash
DEPLOY_DIR="/data/app/releases/current"

# 安全删除脚本
safe_clean() {
  local target="$1"
  # 检查路径不为空且存在
  if [ -z "$target" ] || [ ! -d "$target" ]; then
    echo "无效路径: $target" >&2
    return 1
  fi
  # 检查路径不包含关键目录
  if echo "$target" | grep -qE '^(/|/etc|/var|/usr|/bin|/sbin)$'; then
    echo "危险路径: $target" >&2
    return 1
  fi
  # 干正事
  echo "清理: $target"
  rm -rf "$target"
}

# 调用
safe_clean "$DEPLOY_DIR/cache"
safe_clean "$DEPLOY_DIR/tmp"

这个 safe_clean 函数的思路是:路径为空不删,路径不存在不删,路径是系统根目录不删。加了三层保险。

场景三:crontab 自动清理的谨慎写法

# 不安全的cron写法
0 3 * * * rm -rf /tmp/*

# 安全的cron写法
0 3 * * * cd /tmp && find . -maxdepth 1 -mtime +1 -exec rm -rf {} \; 2>/dev/null

cd /tmp && find ... 比直接 rm -rf /tmp/* 安全多了。即使 cd 失败,find 也不会在错误目录执行。

场景四:用户家目录的迁移和清理

离职人员的家目录清理:

#!/bin/bash
USER="old_employee"
HOME_DIR="/home/$USER"
BACKUP_DIR="/data/archived_homes"

# 先备份,再删除
tar czf "$BACKUP_DIR/${USER}_$(date +%Y%m%d).tar.gz" "$HOME_DIR" && \
  echo "备份完成,等待确认后删除" && \
  rm -rf "$HOME_DIR"

# 备份成功后给个确认提示
echo "备份位置: $BACKUP_DIR/${USER}_$(date +%Y%m%d).tar.gz"
echo "请确认后再手动执行: sudo rm -rf $HOME_DIR"

先备份再删除。数据无价,别贪图方便直接删。

今日作业

你的服务器 /data/logs 目录下有大量历史日志文件,需要删除 90 天前的 *.log.gz 文件。

要求:
1. 先用一条命令查看这些文件的总数量和总大小
2. 写一条安全的删除命令(确保不会误删其他文件)
3. 不能用 rm,而是把文件移动到 /tmp/trash_$(date +%Y%m%d) 目录,确认后再执行 rm

你能写出来吗?注意每一步都要确保路径没错。

E N D

运维少年 · 科技观察

最新文章

随机文章