这个命令是干啥的
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
你能写出来吗?注意每一步都要确保路径没错。