2011年11月14日星期一

换行符引起的麻烦

用sed处理一个hosts文件,内容都是类似下面的行:
203.208.46.180 www.google.com
203.208.46.180 music.google.com
203.208.46.180 music.googleusercontent.com
203.208.46.180 music-streaming.l.google.com
203.208.46.180 large-uploads.l.google.com
IP地址和域名之间是用Tab分割的。想用sed把这个文件处理成dnsmasq的DNS记录配置内容,用如下的命令:
sed -e 's|^\(.*\)    \(.*\)$|address=/\2/\1|g'
两个引用的中间是一个TAB。期望的结果如下:
address=/www.google.com/203.208.46.180
address=/music.google.com/203.208.46.180
address=/music.googleusercontent.com/203.208.46.180
address=/music-streaming.l.google.com/203.208.46.180
address=/large-uploads.l.google.com/203.208.46.180
但实际结果出乎意料:
/203.208.46.180ogle.com
/203.208.46.180google.com
/203.208.46.180googleusercontent.com
/203.208.46.180streaming.l.google.com
/203.208.46.180uploads.l.google.com
不但IP和域名的顺序没有调换,域名前面的几个字符还被覆盖了。后来想是不是因为文件换行符的问题。用Vim打开发现果然是DOS格式的换行符。把脚本改一下就可以得到正确结果了:
sed -e 's/^M$//g' -e 's|^\(.*\) \(.*\)$|address=/\2/\1|g'
错误结果也比较好解释了。Windows下的换行符是Carriage Return + Line Feed(\r\n),Linux下的换行符是Line Feed(\n)。Carriage Return就是打字机的打印头回到行首,Line Feed是指纸出来一行,这样打印头就处在下一行的同一个位置了。sed命令中的第二组引用不但包括了域名,还包括了紧随其后、位于 \n前的\r,这样本来应该位于\2后面的/\1,现在要从\2的开头开始打印了,于是就覆盖了域名的前面几个字符。例如对第一行:
address=/www.google.com/
/203.208.46.180
把开头的address=/www.go都覆盖了,所以结果就是:
/203.208.46.180ogle.com
后面的也都要覆盖掉IP长度+1(/)的字符。

没有评论:

发表评论