sed -i 修改文件的坑

文章讲述了使用sed-i修改线上日志可能导致的问题,即日志追加失败,因为sed会破坏硬链接并创建备份文件。作者提供了查找和恢复未链接打开文件的方法,以及避免此类问题的建议。
摘要由CSDN通过智能技术生成

1 问题描述

开发修改线上日志文件xxx.log,该文件由tomcat实时在写入,执行:

$ sudo sed -i 's/十一月/Nov/g' xxx.log

这行脚本的目的是将日志中的“十一月”替换为“Nov”,执行结束后导致java应用没有再持续写追加任何日志。

2 What happened

2.1 sed in place edit原理

查看sed manpage:

$ man sed

-i[SUFFIX], --in-place[=SUFFIX]

  edit  files in place (makes backup if extension supplied).  The default operation mode is to break

  symbolic and hard links.  This can be changed with --follow-symlinks and --copy.

从中可以得知 sed -i 命令会破坏硬链接和软链接,可以自己测试一下:

$ cat f.txt

aaaaabbbbbb

$ stat f.txt

  File: `f.txt'

  Size: 12         Blocks: 8          IO Block: 4096   regular file

Device: 815h/2069d Inode: 132413      Links: 1

Access: (0664/-rw-rw-r--)  Uid: (  501/    tong)   Gid: (  501/    tong)

Access: 2013-11-10 18:05:24.763863104 +0800

Modify: 2013-11-10 18:05:10.882862432 +0800

Change: 2013-11-10 18:05:10.882862432 +0800

$ sed -i 's/a/b/g' f.txt

$ cat f.txt

bbbbbbbbbbb

$ stat f.txt

  File: `f.txt'

  Size: 12         Blocks: 8          IO Block: 4096   regular file

Device: 815h/2069d Inode: 132406      Links: 1

Access: (0664/-rw-rw-r--)  Uid: (  501/    tong)   Gid: (  501/    tong)

Access: 2013-11-10 18:06:05.677865084 +0800

Modify: 2013-11-10 18:06:02.284864920 +0800

Change: 2013-11-10 18:06:02.285864920 +0800

可以看到,在sed修改f.txt之前,用命令 stat f.txt 显示该文件的 Inode: 132413,而在sed修改之后变成了 Inode: 132406. 这意味这 sed -i 命令实际上删除了原文件,生成了一份同名的修改过后的backup(inode会在下面介绍)。

进一步google “sed -i” 发现,该操作不是真正的 “edit in place”,而是会创建临时文件,之后将临时文件替换原文件,同时调用unlink方法将原文件删除:

sed creates these temporary files when the "in place" (-i) option is turned on. In the normal course, sed actually deletes the files (that is what happens in cygwin) using a call to the 'unlink' library.

sed -i will unlink the file it modifies, so syslog/postfix will continue writing to a nonexistent file.

2.2 unlink打开的文件

介绍相关内容前,先了解下inode是什么。

在linux文件系统中,一个文件的存储由两部分组成,分别是inode和block块。其中block块存储文件的内容,而inode存储的信息包括:文件字节数,文件拥有者的User ID,文件的Group ID,文件的权限,链接数(即有多少文件名指向这个inode),时间戳,block位置等信息。用上文的 stat filename 命令可以查看inode相关信息,这里和本文密切相关的是链接数。

一个inode对应一个文件,但是可以对应多个文件名(有多个链接数),可以用 ln 命令建立链接,用 ls -li 命令查看链接次数变化:

$ ls -li

132406 -rw-rw-r-- 1 tong tong 12 Nov 10 18:06 f.txt

$ ln f.txt f_1.txt

$ ls -li

132406 -rw-rw-r-- 2 tong tong 12 Nov 10 18:06 f_1.txt

132406 -rw-rw-r-- 2 tong tong 12 Nov 10 18:06 f.txt

上面的输出中,132406为inode号,唯一标识了一个inode,可见两个文件名都指向了一个inode。

unlink 操作就是解除一个文件名和inode的对应关系:

$ unlink f_1.txt

$ ls -li

132406 -rw-rw-r-- 1 tong tong 12 Nov 10 18:06 f.txt

当unlink一个打开的文件会发生什么呢?查找unlink系统调用函数,有如下介绍:

unlink() deletes a name from the filesystem. If that name was the last link to a file and no processes have the file open the file is deleted and the space it was using is made available for reuse.

If the name was the last link to a file but any processes still have the file open the file will remain in existence until the last file descriptor referring to it is closed.

也就是说,当文件只有一个链接且没有在其它进程中打开时,unlink操作会删除该文件并释放空间;如果文件只有一个链接,但是unlink操作时该文件在另外一个程序中打开,那么只有当程序操作完成时,该文件才能被删除,即删除操作被postpone了。

因此放在本文的case当中,当对 xxx.log 执行 sed -i 操作后,生成了一个新的xxx.log(inode不一样),而旧的xxx.log被unlink,但直到java应用完成对当天日志的写操作后,旧日志才被删除(也就是日志白写了),从而之后看到的xxx.log没有追加新的内容。

3 Tips1: Finding an unlinked open file

如何查找一个已经被打开,但同时又没有链接数的文件?用 lsof +L1 命令,示例(beta环境):

$ ls -li rt.log

3408701 -rw-r--r-- 1 tomcat tomcat 1271800888 Nov 10 23:26 rt.log

$ sudo unlink rt.log

$ sudo lsof +L1

COMMAND     PID   USER   FD   TYPE DEVICE   SIZE/OFF NLINK    NODE NAME

java      20664 tomcat   45w   REG  252,7 1272466559     0 3408701 /home/q/www/testapplication/logs/rt.log (deleted)

其中NLINK表示链接数,该命令的含义大家可以查看 man lsof.

4 Tips2: Retriving an unlinked open file

假如删除了一个正打开的文件,比如正在写入的日志,有没有办法找回来呢?答案是肯定的,接着Tips1的操作,现在来找回rt.log中的内容:

$ sudo cat /proc/20664/fd/45 > ~/rt_back.log

上例中,20664为之前打开rt.log的PID,45为rt.log在PID中打开的文件句柄。

5 总结

如果再次遇到修改正在写入的日志该怎么做? 暂时想到三种方法,有更好的办法欢迎补充:

1. 用sed -ic xxx.log,其中 -c 选项可以不用破坏文件的软硬链接,修改之后inode不会变,但前提是确保 sed 版本支持 -c 功能;

2. 修改日志文件之前 stop tomcat应用,修改完之后再重启,该方法会影响线上服务,特别是对于单服务而言,不推荐;

3. 等待tomcat写完当天的日志后再进行修改,这种方法最稳妥,但是时效性差。

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值