1. 清空文件,别只知道 > file:从“删内容”到“保 inode 的底层逻辑”
干运维或者天天跟 Linux 打交道的人,迟早都会遇到一个需求:把某个文件的内容清空,但文件本身还要留着。
最常见的场景就是日志文件。/var/log/ 下面那些家伙,动不动几个 GB,磁盘告警了,你得赶紧腾空间。这时候很多人第一反应是 rm -rf 删掉,但删完往往会发现服务还在往这个路径写日志,要么报错,要么悄悄重建一个新文件,权限、属主全变了,后续排查更麻烦。更稳的做法是“清空”而不是“删除”——文件还在,inode 不变,打开那个文件的所有进程都感知不到变化,照常往里写,但磁盘空间立刻释放出来了。
这篇文章就把 Linux 下清空文件内容的几种常用方法都过一遍,从最简单的 shell 重定向,到 truncate、dd、sed,再到 echo 和 cat 的变种玩法。每种方法我都会说清楚它干的是什么、底层发生了什么、适合什么场景、有什么坑。最后再补充一些我在实战里踩过的坑和总结出来的检查技巧,争取让你看完就能直接用,不用再一个个去试。
先说个插曲:我见过不少新手(甚至一些写了几年脚本的人)以为“清空文件”就是把文件删了再 touch 一个。这个操作在多数时候能骗过眼睛——ls -l 看到文件还在,大小是 0——但如果你那个文件被某个进程打开了,删掉再新建,文件描述符指向的还是原来那个已经被删除的 inode,你新 touch 出来的文件跟那个进程一点关系都没有。等进程往里面写日志,老的 inode 空间又被写出来了(对,删掉的文件只要有句柄持有,空间并不会立刻释放,进程还能继续写),心里想的是“我都清空了怎么磁盘还是满的”,实际上就是没搞懂 inode 和文件描述符这层关系。所以下面讲到每种方法时,我都会特别强调它对 inode 的影响,这是选型时的核心依据。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 空文件?文件大小是 0 不等于文件被清空了
在正式介绍清空方法之前,先把“空文件”这件事的定义捋清楚,否则后面很多操作会把人绕晕。
Linux 下一个普通文件由三部分构成:目录项(dentry)、inode 和数据块。目录项记录文件名和 inode 的对应关系,inode 保存元数据(权限、属主、时间戳、数据块指针列表等),数据块才是真正存内容的地方。当我们说“清空文件内容”,本质上有两种理解:
一种是把数据块里的内容全部释放,inode 里的文件大小字段写成 0,但保留 inode 和目录项的关联——文件还在,只是内容没了。这就是最标准、最推荐的做法。
另一种是“内容还在磁盘上,但用户看不到了”,典型操作是删除目录项,让 inode 失去引用,文件进入“不可见但可能还被进程持有”的状态。这不是清空,是删除,很多问题的根源就是有人把这两件事混为一谈。
所以我们在判断清空是否成功时,不能只看 ls -l 输出的文件大小是不是 0。文件大小为 0 只说明“通过路径看到的大小是 0”,如果这个文件被某进程用追加模式打开着,进程照样可以在”大小为 0“的文件里继续写入——写入后大小又会变回去。正确的检查方式是 ls -l 看大小、df -h 看磁盘空间,还要注意被占用的空间是否真的释放了。有些场景下文件大小是 0,但 lsof 能看到某个进程持有已删除文件的句柄,磁盘空间迟迟不降,这时候你清空了普通路径上的文件也没用。
还有更特殊的情况:有些文件看起来大小是 0,其实里面存的是稀疏文件(sparse file)的元数据空洞,du 看到的块占用和 ls 看到的大小完全对不上。清空稀疏文件时,用重定向 > file 这类方法通常会改变文件的属性状态,而用 truncate -s 0 处理稀疏文件,则是直接对 inode 上的 size 字段动手,本质上更贴近“清空”的语义。
所以这篇文章讨论的“清空文件内容”,定义非常明确:让文件大小归零,数据块尽快释放,inode 和目录项保留,不影响正在使用该文件的进程。下面的每一种方法,我都会从这几个维度来做对比。
3. 先认识五个清空工具:适用场景和优缺点一览
在实操环节展开之前,我先把主流的清空方法列成一张表,给你一个整体印象。后面再逐个细讲底层原理和踩坑细节。
| 方法 | 核心命令 | 对 inode 影响 | 释放磁盘空间 | 大文件效率 | 适用场景 |
|---|---|---|---|---|---|
| Shell 重定向 | > file |
不改变 | 立即释放 | 极快,不读内容 | 日常最快最常用 |
/dev/null 重定向 |
cat /dev/null > file |
不改变 | 立即释放 | 极快 | 老派写法,语义清晰 |
echo -n |
echo -n > file |
不改变 | 立即释放 | 极快 | 空输出,写法直观 |
truncate |
truncate -s 0 file |
不改变 | 立即释放 | 极快,不读内容 | 大文件首选,语义明确 |
dd |
dd if=/dev/null of=file |
不改变 | 立即释放 | 极快 | 需要统计信息或特殊场景 |
sed |
sed -i 'd' file |
不改变 | 可能延迟释放 | 中等,需读文件 | 需要复杂过滤条件时用 |
| 删除重建 | rm file && touch file |
改变 | 有句柄时延迟释放 | 极快 | 不适合日志类、共享类文件 |
这张表是最佳实践的浓缩版,但光看表不够,知道怎么用只是第一步,理解背后的原理才是这篇文章的核心。下面一个一个拆开讲。
3.1 Shell 重定向:> file 为什么最快
这是 Linux 下最经典、最快速的清空方式,也是我平时用得最多的。命令极简:
bash复制> /var/log/nginx/access.log
如果你在 bash、zsh 里执行这行,shell 会以“截断到 0”的方式打开这个文件(O_TRUNC 标志),瞬间把文件大小变成 0。它的底层机制是:打开文件时告诉内核“把老内容全部丢弃”,内核直接释放掉这个 inode 关联的所有数据块,然后把文件大小字段更新为 0。因为整个过程没有读入任何数据,所以文件即使有几十 GB,清空也是在毫秒级完成的。
实际使用中要注意几个细节:
- 当前的 shell 用户必须有该文件的写权限,否则会报
Permission denied。如果是 root 操作的日志文件,别忘了sudo。 - 这一步会立刻释放磁盘空间,不需要额外的
sync,文件系统会异步把更新落盘,但空间计数是即时生效的。 - 对正在被进程使用的文件同样适用。比如 nginx 的 access.log,进程打开的是 inode,你重定向截断之后,nginx 继续往同一个 inode 写日志,不会报错,文件会从 0 开始继续累积。
- 重定向前之前注意确认拼写。
> /dev/sda这种操作会把整个磁盘设备的内容清空——虽然原理一样,但后果完全不一样。真要手滑了,神仙都救不回来。清空前多看一眼路径,这是用任何“清空”命令都必须养成的习惯。
还有一个偏门的变种,利用重定向在子 shell 里执行:
bash复制: > /path/to/file
这里的 : 是 shell 内置的空命令,什么也不做,但重定向的截断动作照样执行。效果跟 > file 完全一样。有些人写脚本时习惯用 : > file 来强调“我就是要清空文件,而不是误写了个东西进去”,这个写法在老派运维圈子里比较常见,你可以按自己团队的风格来。
3.2 /dev/null:把“空”体现得最优雅的写法
第二种经典写法是:
bash复制cat /dev/null > /var/log/messages
这个命令的语义非常直白——把 /dev/null(一个永远返回空内容的特殊设备文件)的内容复制到目标文件里。因为输入是空的,输出自然也是空,文件就被清空了。
在 Linux 内核里,/dev/null 是个字符设备,主设备号 1,次设备号 3。读它的时候,驱动直接返回 EOF,什么数据都不会给;写它的时候,驱动无条件丢弃,永远不占空间。所以我们用 cat 读 /dev/null 是零开销的,再重定向到目标文件,整个过程基本就是一次打开文件、截断、关闭的操作,单纯从性能上看和 > file 没有本质差别。
这个写法好处有两个:
- 可读性好,代码里一目了然,注释都能省了。
- 兼容性极佳,在 POSIX shell、csh、甚至老的 sh 里都能稳定执行,不会因为 shell 解析差异而出问题。
我见过一些老系统上的脚本习惯用这种方式,因为早期某些 shell 对 > file 这个裸重定向的解析会有歧义(比如你把 > 单独放在命令开头,有的老解释器不认),但 cat /dev/null > file 的写法靠命令 + 重定向双保险,几乎没有兼容性问题。
不过有一类特殊情况要留意:某些文件系统或伪文件(比如 /proc 下的某些只读文件、某些 sysfs 管理节点)对写操作有特殊逻辑,你重定向进去的数据会被无视,cat /dev/null > 这种操作也可能会因为设备的特殊行为而报错。所以如果是清空 /sys 或 /proc 下的文件,先确认它到底是普通文件还是内核接口,别上来就清。
3.3 echo -n > file:最直观的“写入空字符串”
bash复制echo -n > /tmp/empty_me.log
echo 不带任何参数,只输出一个换行符;echo -n 连换行符都不输出,相当于输出一个长度为 0 的字符串。重定向到文件里,效果就是文件被截断成 0 字节。
这个写法的理解成本和cat /dev/null差不多,但有一个很多人不知道的细节:如果直接用 echo > file(不加 -n),文件的“内容”就不是 0 字节,而是一个换行符——大小变成 1。这是无数新手踩过的坑:跑完 echo > file 以为清空了,ls -l 一看,文件大小是 1,里面有个 \n。
所以在脚本里如果要追求绝对的“0 字节”,务必写成 echo -n > file。不过说实话,在绝大多数日志清理场景里,文件里残留一个换行符几乎没有任何影响,下次进程追加日志时会紧跟在换行符后面写,不会多出问题。但如果你在生成配置文件、模板文件,或者对文件内容有严格校验的场景,一定要用 -n 保证真空。
还有一点:echo 是 shell 内建命令,不同 shell 对 echo -n 的行为解释并不完全一致。bash 里 -n 是“不输出换行”;但在某些 POSIX 模式下,echo 可能默认把 -n 当成普通字符串输出(比如 dash 的部分版本行为就不太一样)。如果你写的是跨 shell 的脚本,建议用 printf '' > file 代替,可移植性更好:
bash复制printf '' > /tmp/empty_me.log
printf 不带格式参数,输出就是空字符串,语义上比 echo -n 更严谨。
3.4 truncate:清空大文件的“医疗级工具”
前面几种方法本质上都是“用重定向的方式把一个空的东西写进去”,而 truncate 是真正意义上的“外科手术”——直接修改 inode 里记录的文件大小字段,根本不管数据块里的内容。
bash复制truncate -s 0 /var/log/mysql/error.log
这条命令把文件大小设置为 0。内核收到这个请求后,会摘掉该 inode 关联的所有数据块,释放磁盘空间,然后把大小字段归零。对比重定向方案,核心优势在于完全不需要依赖 shell 的重定向解析,在脚本里、在程序里、在 cron 任务里,都不会有“误开文件”的问题。
truncate 还能干很多重定向做不到的事,比如把文件设置成任意指定大小,而不只是 0:
bash复制# 把文件扩容到 100MB(稀疏文件,不实际占磁盘)
truncate -s 100M /tmp/test.img
# 把文件缩到 1MB
truncate -s 1M /tmp/test.img
但这种用法在“清空”场景下基本用不到,我们更关心的是 -s 0 的语义。
实战中我会在所有需要写脚本自动清理日志的地方,优先考虑 truncate。原因很简单:重定向的 > file 在脚本里执行时,如果文件不存在,shell 会直接帮你创建一个空文件。这个行为在一些场景下是好事,但在清理日志时可能是坏事——比如定时任务里路径写错了,它不会报错,而是默默创建一个名字错乱的空文件,真正的日志文件岿然不动。truncate 则不同,目标文件不存在时会报错,能第一时间提醒你“路径有问题”。
另外,truncate 对“按需延迟分配磁盘”的文件系统(比如 ext4 的 delayed allocation、XFS 的 COW 等)处理得也更干净,某些极端情况下重定向截断后,文件系统可能还会在后台残留一些延迟释放的块,而truncate 在内核里直接走 truncate 系统调用,对文件系统元数据的处理更规范。当然这个差异在日常场景里感知不强,但遇到用 XFS 或者 btrfs 的生产环境,truncate 是更稳妥的选择。
再提一个很实用的组合场景:配合 find 按时间、大小筛选日志文件然后统一清空:
bash复制find /var/log -name "*.log" -size +100M -mtime +7 -exec truncate -s 0 {} \;
这条命令会把 /var/log 下超过 100MB 且修改时间超过 7 天的日志文件全部清空,不删除文件,不重启服务。我经常在运维巡检任务里用类似写法处理一些不常打日志但偶尔爆量的应用目录。
3.5 dd 和 sed:两个“能清但用得少”的工具
dd 清空文件的方式也很有趣:
bash复制dd if=/dev/null of=/tmp/old.log
if 指定输入文件,/dev/null 提供空数据;of 指定输出文件。dd 在打开输出文件时默认带了 O_TRUNC,所以哪怕输入是空的,输出文件也会被截断成 0。这条命令跑完会输出一段统计信息,告诉你复制了 0 字节之类,对于有强迫症的人来说反而能看到明确反馈。
不过说白了,dd 干这事有点杀鸡用牛刀。它的强项是低级复制,比如备份磁盘、分区镜像、跳过指定字节数等场景。清空文件这个任务上,dd 和前面几种没有本质区别,唯一的优势是“卸载了输出文件的内容后再清零”这个操作可以在一条命令里完成,不用先 truncate 再 dd 什么的。真实环境里我很少用它来清空文件,除非目标是个字符设备或者块设备——比如要清空一个块设备的前 1MB:
bash复制dd if=/dev/zero of=/dev/sdb1 bs=1M count=1
这种情况下dd才是不可替代的,但这是覆盖写不是清空,跟本文主题已经跑偏了,不展开。
再说 sed。sed -i 'd' file 这个写法看着没问题,但底层实现是:sed 创建一个临时文件,逐行读取源文件,过滤掉匹配的行后写进临时文件,最后用临时文件替换原文件。当你的过滤规则是“删除所有行”时,源文件每一行都会被过滤掉,临时文件是空的,替换之后原文件就空了。
这个方式的代价是:sed 会完整读取一遍文件内容,哪怕内容是 10GB,它也得老老实实读一遍才能逐行丢弃。对大文件来说,这个效率远低于 truncate 和重定向。反而sed的价值在于“按内容清空”——比如你只想清掉文件中匹配某种模式的内容,而保留其余部分:
bash复制sed -i '/^DEBUG/d' /var/log/app.log
这才是它的主战场。纯粹清空文件用 sed,基本属于大材小用,不推荐。还有一点要提醒:sed -i 在不同实现下(GNU sed 和 BSD sed)的参数行为不完全一致,BSD sed 要求你写 -i '',否则会报错。跨平台脚本里用 sed -i 要特别小心。
4. 实战:日志清空之后,如何确认空间真的释放了
前面讲了一堆原理和命令,现在进入实战环节。我拿一个最常见的场景来串一遍:一台生产服务器上,nginx 的 access.log 已经涨到 15GB,磁盘告警要处理。我来给你演示一下完整的操作流程和检查思路。
4.1 第一步:定位大文件,搞清楚它是谁在写
先确认到底是哪个文件占了空间:
bash复制du -sh /var/log/nginx/*
df -h
如果有必要,再确认是哪个进程打开着这个文件:
bash复制lsof /var/log/nginx/access.log
lsof 的输出会告诉你哪些进程持有这个文件,进程号是多少,打开模式是读还是写。这一步非常关键——如果文件被进程以追加模式(O_APPEND)打开,清空以后进程会继续在文件尾部写入,这是正常的;如果进程是随机写模式(比如某些数据库,如 SQLite、MySQL 的某些表空间文件),清空会立即导致程序报错甚至崩溃。所以在清空任何非日志类文件之前,一定先确认这个文件是不是“只追加”类型。日志文件绝大多数都是追加写,安全;数据库文件绝对不是,不能乱清。
4.2 第二步:选择清空方式并执行
确认是纯日志文件后,我一般会这么选:
- 手头临时清理:直接
> /var/log/nginx/access.log,最快最省事。 - 写进脚本定期执行:用
truncate -s 0 /var/log/nginx/access.log,因为脚本里如果路径写错,truncate 会报错提醒。 - 需要更清晰的日志记录:写进 cron 前先执行
truncate -s 0,并在日志里留一条“success”记录。
实际执行:
bash复制truncate -s 0 /var/log/nginx/access.log
注意,执行时如果没有报错,那就说明 truncate 成功了。
4.3 第三步:验证文件大小和磁盘空间
清空后必须验证两个层面,缺一不可:
bash复制ls -lh /var/log/nginx/access.log
df -h /
ls -lh 应该显示文件大小变成 0,但这只是文件层。df -h 确认磁盘使用率降下来了,因为只有当内核释放了文件占用的所有数据块,磁盘空间才算真的归还给文件系统。在某些文件系统上(比如 NFS、ZFS),释放可能有延迟,df 不会立刻降到预期值。这时候不用慌,等几秒再执行一次 sync; df -h,通常能追上。
还有一个容易忽略的点:如果 access.log 被 nginx 打开着,ls -l 显示的大小是 0,但 nginx 会继续往同一个 inode 里写新日志。清空后立刻有请求进来,文件的大小会从 0 变成几百字节——这是正常的,说明清空动作没有影响进程的正常写入。这不是失败,是成功。很多人第一次看到这种情况还会以为清空没生效,白白折腾。
4.4 第四步:清理需谨慎——权限、属主、selinux 上下文
清空文件的执行用户必须有写权限。如果文件属主是 root,自己用的普通用户跑 > file 就会 Permission denied。这在处理系统日志时最容易踩,解决方式是用 sudo 执行。注意,用 sudo 清空时,清空后的文件属主、权限、ACL 都不会变——因为 inode 没变。这也是清空优于删除重建的一个重要原因。
如果是带 SELinux 的环境(比如 CentOS、Rocky Linux),清空操作不会改变文件的 SELinux 上下文,服务进程继续写日志不会碰到 AVC 拒绝。但如果你用 rm 删了再建一个新文件,新文件的 SELinux 上下文很可能是从父目录继承的默认值,跟原先配置的不一致,可能触发权限问题。这一点在生产环境里会让人排查到怀疑人生,用清空而不是删除,就是规避这类问题最省力的手段。
4.5 实操复盘:一次完整的日志清空记录
下面这段是我在测试服务器上实际跑过的过程,贴出来给你参考:
bash复制# 1. 查看文件现状
[root@dev-server ~] ls -lh /var/log/nginx/access.log
-rw-r--r-- 1 root root 15G Mar 3 10:35 /var/log/nginx/access.log
# 2. 查看谁打开着这个文件
[root@dev-server ~] lsof /var/log/nginx/access.log
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 root 11w REG 253,0 15728640000 262146 /var/log/nginx/access.log
# 3. 执行清空
[root@dev-server ~] truncate -s 0 /var/log/nginx/access.log
# 4. 验证文件大小
[root@dev-server ~] ls -lh /var/log/nginx/access.log
-rw-r--r-- 1 root root 0 Mar 3 10:36 /var/log/nginx/access.log
# 5. 验证磁盘空间
[root@dev-server ~] df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 18G 20G 48% /
注意到第 3 步之后,nginx 的 PID 没变,文件句柄没重建,进程完全无感知——这就是清空文件的精髓。如果用的是 rm 再 touch,nginx 继续往已被删除的 inode 里追加日志,磁盘空间不会释放,而新文件慢慢又被 nginx 打开,你等于同时养了两个日志文件,磁盘空间不降反增,那才叫灾难现场。
5. 从“清空文件”延伸到“释放空间”:被占用空间为什么清不掉
到这里,“清空文件内容的几种方法”其实已经讲完了,但我还想再深入一层——因为很多人清空完文件之后会发现一个问题:文件大小是 0 了,df -h 一看磁盘空间还是没降多少,甚至完全没降。这种情况十有八九是“文件被进程删除了,但进程还持有句柄”或者“文件被清空了,但某个文件系统快照、备份进程还拽着老数据”。
5.1 已删除但仍被占用的文件
典型场景:某个应用会定期自己 rotate 日志。凌晨 logrotate 把它自己的日志 mv 走了,然后新建一个同名空文件。应用进程继续往旧日志的 inode 里写——但旧的 inode 已经不在任何目录里了,它的文件大小不会显示在 ls -l 任何路径下面,可它占用的数据块并没有释放。你执行 ls -l /var/log/app.log 看到的是新文件,挺小,df 却依然报警。
这时候正确的排查姿势是:
bash复制lsof +L1
这个命令会列出所有被删除但仍被进程打开的文件(+L1 意思是列出 link count 小于 1 的文件)。找到 PID 后,若是日志文件,常规做法是让进程重新打开日志(比如发送 USR1 信号给 nginx、USR2 给 Apache 等),或者直接重启服务。少部分情况允许你直接清空那个已删除的 inode——但你已经找不到它的路径了,只能通过 /proc/<PID>/fd/<FD> 来操作:
bash复制ls -l /proc/1234/fd/11
: > /proc/1234/fd/11
这一招可以“隔空清空”那个已删除但仍被持有的文件,释放空间,且不需要重启进程。我处理过几次这种场景,都是用 /proc 下的 fd 路径解决的。不过要提醒一句:前提是你非常确定那个 fd 对应的是日志文件且可以清空,千万不能对数据库数据文件的 fd 做这种操作。
5.2 清空大文件后,文件系统还是满的?
还有一种情况:文件本身是稀疏文件(sparse file),ls -l 显示大小好几个 TB,但实际占用磁盘只有几 MB。你把这种文件 truncate -s 0,df 本来就不会有大变化——因为它本来就没占用多少真实磁盘。判断文件真实占用要看 du:
bash复制du -h /path/to/bigfile
如果 ls -lh 显示 5T 而 du 只有 20M,这就是典型的稀疏文件。清空它不会释放多少空间,磁盘满的问题也不该找它。搞懂 ls 和 du 的区别,能让你在排查磁盘告警时少走很多弯路。
5.3 快照、备份对空间释放的影响
如果在 LVM、btrfs 或 ZFS 上做了快照,即使文件清空、数据块释放了,快照里依然记录着旧数据块。此时 df 看到的空间可能没有立刻回来——因为快照占着那些块。这种情况下,清空文件确实成功了,但你需要删除或合并快照才能真正拿回空间。
这类问题跟清空命令本身无关,但属于“为什么空间没释放”的高频原因。我在文章里特意提出来,就是为了让你在实操时不要被表象迷惑,把锅甩给清空命令本身。
6. 常见问题速查:清空文件时容易踩的 6 个坑
把实战中反复遇到的问题整理成一张速查表,方便你直接对照。
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
> file 报 Permission denied |
当前用户无写权限 | 检查文件属主,用 sudo 或者切换属主 |
echo > file 后文件大小为 1 |
echo 默认输出换行符 | 改用 echo -n > file 或 : > file |
| 清空后磁盘空间没降 | 有进程持有已删除文件句柄 | lsof +L1 排查,通过 /proc/PID/fd 清空或重启进程 |
| 清空数据库文件后服务崩溃 | 数据库数据文件不是追加写模式 | 绝不能用清空方式处理数据库文件,应通过数据库工具操作 |
>/dev/sda 手滑把设备清了 |
重定向目标写错 | 无解,只能靠备份恢复——所以命令前多看一眼路径 |
脚本里 > file 自动创建了错误文件 |
路径写错,shell 自动创建空文件掩盖问题 | 用 truncate -s 0,文件不存在会报错 |
| 清空 NFS 上的文件后 df 很久才刷新 | NFS 服务端缓存/客户端缓存 | 稍等并 sync,确认服务端是否正常 |
| 文件是符号链接,清空了链接指向的文件,链接本身还在 | 重定向会跟随符号链接 | 确认目标路径是否为链接文件,按需处理 |
这些坑里,最阴险的是第二个——文件大小 1 个字节,不仔细看根本发现不了。我在教新人写清理脚本时都会特意强调这一点:清空文件完成后必须用 ls -l 或者 stat 验证大小,而不是肉眼瞄一眼目录列表就收工。哪怕只残留一个换行符,在某些严格审核的场景(比如配置文件、版本包生成)里都算事故。
7. 场景化选型建议:不同场景该用哪种清空方式
把全文内容压缩一下,给出场景化建议,方便你直接对号入座。
- 日常命令行几秒钟清空一个日志文件:
> /var/log/xxx.log,简单直接。 - 长期定时任务、cron 脚本:
truncate -s 0,路径写错会报错,安全性更高。 - 跨 shell 可移植性强:
printf '' > file或cat /dev/null > file。 - 对文件内容有严格 0 字节要求:确认清空后立刻
stat -c %s file检查大小。 - 文件很大,几十 GB:
truncate -s 0或者> file,两者都快,但 truncate 的语义更安全。 - 需要“按内容过滤清空”:
sed -i,但这属于另一种需求了,不是本文主题。 - 只想清空文件的一部分(比如前 1GB):
dd if=/dev/zero of=file bs=1M count=1024 conv=notrunc,但这需要确认文件系统的支持,且操作有风险,不推荐新手做。
还有一个实用的点:当你处理的是日志文件且担心未来会不断膨胀时,除了清空,更正规的做法是配置 logrotate。它可以根据大小、时间自动轮转和压缩,保留最近 N 份日志,老日志自动删除。清空文件是“应急手段”,logrotate 是“长期方案”,两者不冲突。
8. 最后分享一个我自己的小习惯
干这行越久,越发现“清空文件”这种小事里藏着大学问。说一个我自己的习惯性动作,也算给大家的额外赠品。
在我管理的所有 Linux 服务器上,我会在 root 用户的 .bashrc 里加一个函数:
bash复制truncate_log() {
if [ -z "$1" ]; then
echo "Usage: truncate_log /path/to/file"
return 1
fi
if [ ! -f "$1" ]; then
echo "Error: $1 is not a regular file"
return 1
fi
truncate -s 0 "$1"
ls -lh "$1"
}
以后清空日志就是一条命令的事,还自动帮你验证了清空结果。这个函数虽然简单,但配合前面讲的“truncate 报错机制”,基本杜绝了脚本里手滑路径的问题。
另外每次执行完清空动作,我都习惯顺手执行一下 df -h,确认空间确实回来了。不是不信任命令,而是这套习惯形成肌肉记忆后,在真正的生产故障现场才能不慌不忙地连环操作。命令谁都会敲,区别在于敲完之后你还知不知道发生了什么、验证了什么、下一步该看什么。希望这篇文章能帮你把最后这点补齐。
