首页 > 代码库 > 在CentOS 5.5上使用sed遇到的一个bug
在CentOS 5.5上使用sed遇到的一个bug
在 CentOS 5.5 上使用 sed 遇到一个bug
$ 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 时
下载它的源代码 https://google-search-appliance-mirror.googlecode.com/files/sed-4.1.5-5.fc6.src.rpm
解压开可以看到redhat在sed-4.1.5上打上了3个补丁
sed-4.1.5-bz185374.patch
sed-4.1.5-relsymlink.patch
sed-4.1.5-utf8performance.patch
redhat为什么要为sed打上补丁呢,这源于另外一个问题,当sed -i do_sth file时实际的过程是这样的
sed do_sth file > tmp mv tmp file
那么问题来了,如果file是指向文件file2的软连接,sed -i do_sth file 的结果是:
sed "剪短" 了 file 到 file2 的软连接 file2 的内容不会改变 file 成为一个file2的副本 它的内容改变了 (如果do_sth会修改)
这本身不是一个大问题 (算 sed 的一个 "小坑"吧)
不过有人试图解决这个问题, 所以就有了 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 的行为和之前版本一致
在CentOS 5.5上使用sed遇到的一个bug