在 CentOS 5.5 上使用 sed 遇到一个bugphp
$ echo AAA > config $ ln -s config cfg $ sed -i 's/AAA/aaa/' cfg sed: ck_follow_symlink: couldn't lstat c/config: No such file or directory
这个bug发生在 sed -i do_sth symbolic_links_without_slash 时app
下载它的源代码 https://google-search-appliance-mirror.googlecode.com/files/sed-4.1.5-5.fc6.src.rpm函数
解压开能够看到redhat在sed-4.1.5上打上了3个补丁google
sed-4.1.5-bz185374.patchspa
sed-4.1.5-relsymlink.patch指针
sed-4.1.5-utf8performance.patchcode
redhat为何要为sed打上补丁呢,这源于另一个问题,当sed -i do_sth file时实际的过程是这样的orm
sed do_sth file > tmp mv tmp file
那么问题来了,若是file是指向文件file2的软链接,sed -i do_sth file 的结果是:字符串
sed "剪短" 了 file 到 file2 的软链接 file2 的内容不会改变 file 成为一个file2的副本 它的内容改变了 (若是do_sth会修改)
这自己不是一个大问题 (算 sed 的一个 "小坑"吧)it
不过有人试图解决这个问题, 因此就有了 sed-4.1.5-bz185374.patch
这个补丁里面新增了一个函数 ck_follow_symlink
它核心的地方是使用readlink
好比 文件 a -> b 文件 b -> c
ck_follow_symlink可以成功的寻迹到最终的文件 a -> c
不过 ck_follow_symlink 在有种状况下不能正常工做
文件 a -> ../b 而文件 b -> c
也就是说 a -> ../c 而 ck_follow_symlink 把它理解为了 a -> c
也就是说新引入了一个问题
为了解决这个新问题,因此又有了补丁sed-4.1.5-relsymlink.patch
这个补丁就是为了解决 a -> ../b 且 b -> c 这样的场景
解决方案是这样的:(补丁片断)
//buf 是记录当前文件名的buffer err = readlink (buf, buf2, bufsize); buf2 [err] = '\0'; if (buf2[0] != '/') { dir = dirname (buf); // dir part of orig path int len = strlen (dir); // orig path len buf[len] = '/'; strncpy (buf+len+1, buf2, bufsize - len - 1);
那么问题来了,dirname 是怎么工做的
char ch[] = "/tmp/123"; char * p = dirname(ch);
dirname(ch) 的行为是 把 ch[4] 从 '/' 改为了 0 并把ch的地址做为返回值
但当 char ch[] = "tmp"时 dirname(ch) 的行为又是如何呢?
答案是 不对ch作任何修改 dirname(ch) 返回一个指针 这个指针指向一个常字符串 "."
这里对dirname的使用是错误的, 他错误的认为dirname必定会去修改入参并返回入参地址
因此对dirname的错误使用引入了文章开头的那个bug
这个bug在存在于CentOS/RHEL/Fedora这一支的某些版本中, 有无最终修复未考证
对与sed主线,它没有这个问题,对于sed -i 做用与软链接 "剪短" 软链接这个问题
在4.1.5以后的 sed 加了一个新参数--follow-symlinks,它能够保证sed -i 时不“剪断”软链接
没有这个参数时 sed -i 的行为和以前版本一致