sed命令临时文件空间占用的问题

本文同步自(最佳显示效果请点击):https://zohead.com/archives/sed-space-usage/

sed 是 Unix(Linux) 系统中经常使用的编辑命令,常被用来修改编辑替换文件中的数据。我在平时也经常用到 sed 命令,最近遇到一个比较奇怪的空间占用的问题:

在类似嵌入式的使用环境中有一个比较大的文本文件,由于环境所限,此文件所占用的空间也有限制,假设最多为 20MB,超过 20MB 将导致系统中其它文件没有空间,后续修改其它文件会报:Not enough space 错误。

因此我需要定期对这个文件大小进行监控,如果超过 20MB 那就用 sed 命令删除其中的数据,每次删除 5000 - 10000 行直至文件大小不超过 18MB(留一定的余量)。

定时监控的脚本操作就类似这样(假设 /var/myfile 为监控文件):

while [ 1 ]; do
   # get file size
   FILE_SIZE=`wc -c < /var/myfile`
   if [ $FILE_SIZE -le 18874368 ]; then
      break
   fi
   sed -i '/1,5000d' /var/myfile
   sleep 600
done

上面的脚本会每 10 分钟检测一次,如果超过 18MB 就用 sed 的 “-i” 参数自动删除文件开头的 5000 行,这看起来是能起到监控的作用的,但实际运行中,/var/myfile 的空间并没有被释放,而且系统被完全占满了,同时 /var 目录下还产生了一堆文件(有几百个之多,没有完全列出来):

[root@server ~]# ls /var
myfile sedBu2rt9 sed4l4DpD sedXoiew0 sed09rwbn sedHc0987
...

这明显就是 sed 命令产生的临时文件了,但为什么没有自动删除的,而且产生了这么多个。

经过详细查询 sed 命令的帮助信息,终于知道了原因:

sed 的 “-i” 参数表示 edit files in place,熟悉 sed 命令的朋友应该知道不加 “-i” 参数时对文件的修改操作是直接写到 stdout 标准输出并不修改文件的。

sed 的 “-i” 参数做的操作就是将原先修改的 stdout 输出先保存成一个临时文件,并且临时文件与原始的文件在同一目录下(通过 mktemp 之类的方法生成,也就是上面看到的 sedXXXXXX 这种形式的临时文件,假设为:/var/sedBu2rt9),完成之后再将临时文件重命名覆盖为原始的文件(相当于:mv /var/sedBu2rt9 /var/myfile 操作),这样临时文件就消失了,也可以看出 “-i” 参数并不是直接修改 /var/myfile 文件本身。

在正常情况下,上面的流程是没问题的,但当系统空间不够这种极端情况时,sed 第一步将 stdout 输出保存到临时文件就会失败(空间已经不足),后续的重命名覆盖为原始的文件当然并不会做,但是悲剧的是 sed 在这种失败情况下连产生的错误临时文件也不删除,结果空间不足的情况会更加严重(第一步可能写了一部分数据到临时文件),而且会留下来一堆没用的临时文件。

另外 sed 命令的 “-i” 参数还有一个潜在的问题:

从上面的流程我们可以看出,通过 “-i” 参数修改文件之后,源文件的 inode 已经发生了变化(不是真正原始的文件了),如果此前有其它程序已经打开了该文件,后续读取会失败,而且这种情况下由于原始的 inode 还在被使用,虽然 inode 已经无效了,但对文件系统的占用并没有被释放,因此像这种源文件同时被其它程序打开的情况下,还必须关闭重启此程序或者通知程序重新打开文件才能真正释放文件系统的空间。

因此对于 sed 的这种情况,我们可以通过其它的程序来修改文件,也可以单独开辟别的空间(例如 tmpfs 之类)先拷贝一份通过 sed 命令进行修改,然后再拷贝覆盖回源文件。

以上是根据个人实际使用中所遇到的情况总结出来的,有任何问题欢迎提出指正哦 ^_^

1 条评论 在此博文.
  1. Kendra Fondren:

    This is cool!

发表评论





*