在 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 的行爲和以前版本一致