昨天,同事告诉我发现一个诡异的问题,grep 无法搜索 shell 中的变量,着实很惊讶。到他所说的服务器上试了下,还真是不行!
大概就是这样一个要求:
①、有个文本为 userid.txt,里面每一行一个用户 id,类似如下:
0001 0003 0005 0007 0009
②、另外还有一个文本为 record.txt,里面是所有用户的操作记录,一行一条,并且包含有 id,类似如下:
[12 11 2014 11:03,198 INFO] userId:0001 gilettype:3 [12 11 2014 12:12,198 INFO] userId:0002 gilettype:3 [12 11 2014 13:02,198 INFO] userId:0003 gilettype:1 [12 11 2014 14:33,198 INFO] userId:0001 gilettype:3 [12 11 2014 15:13,198 INFO] userId:0002 gilettype:2 [12 11 2014 16:43,198 INFO] userId:0003 gilettype:1 [12 11 2014 17:32,198 INFO] userId:0001 gilettype:3 [12 11 2014 18:16,198 INFO] userId:0002 gilettype:1 [12 11 2014 19:25,198 INFO] userId:0003 gilettype:2
③、现在他要求循环取出 userid.txt 中每一行 ID 值,然后去 record.txt 去查找并保存结果。
实现这个需求原本很简单,根本难不倒他,只要使用 while read + grep 就能搞定。可问题是明明 record.txt 里面包含这些 id,却无法输出结果??
我顺便写了一个测试脚本测试了下:
#!/bin/bash while read userId; do echo $userId grep $userId record.txt done <userid.txt
发现脚本可以打印 echo $userId,却无法 grep 到??而实际上 record.txt 里面是有这个 id 的!还真诡异!
先百度搜索了一下【grep 无法搜索变量】,还真有不少类似问题,比如:http://bbs.chinaunix.net/thread-123113-1-1.html
根据经验,对于这种诡异的问题,我首先会想到是不是系统有问题,要是系统有问题你怎么折腾都是错!
于是把他的文件拷贝到其他服务器,发现居然可以了!!!难道真是系统问题么?
第一台是 SUSE Linux,第二台是 Centos,难道和系统发行版有关系?
后来,同事在第二台服务器上完成了他的项目。但这个问题却一直留在我的脑子里,挥之不去。
今天,我决定再次研究下这个问题,看看是不是有其他原因。我先在那台 SUSE Linux 上,手工编写所需文件:
[[email protected] ~]# vim 1.txt
1111 3333 5555
[[email protected] ~]# vim 2.txt
1111 2222 3333 4444 5555 6666
[[email protected] ~]# vim test.sh
#!/bin/bash cat 1.txt|while read userId; do grep $userId 2.txt done
结果,发现居然可以输出结果!证明这系统没有问题啊!于是再一次测试了一下昨天的脚本,发现还是无法输出。
于是使用 -x 参数 调试一下脚本:
先修改脚本代码:
#!/bin/bash cat userid.txt|while read userId; do grep $userId record.txt sleep 3 done
然后,带 -x 参数执行:
[[email protected] ~]# sh -x test + cat userid.txt + read userId + grep $'0001\r' record.txt + sleep 3 + read userId + grep $'0003\r' record.txt + sleep 3 + read userId + grep $'0005\r' record.txt + sleep 3
难怪找不到,grep 的变量已经变了!0001 变成了 $'0001\r' !
看到\r,立马想到是文本中的换行符,可为毛会输出换行符呢?想到博客以前写的《Linux 终端:用 cat 命令查看不可见字符》,继续改了一下代码:
#!/bin/bash cat -A userid.txt|while read userId; do grep $userId record.txt sleep 3 done
执行后恍然大悟:
[[email protected] ~]# sh -x test + cat -A userid.txt + read userId + grep '0001^M$' record.txt + sleep 3 + read userId + grep '0003^M$' record.txt + sleep 3 + read userId + grep '0005^M$' record.txt + sleep 3
原来是 dos 下的文本格式,问了下同事,他还真是从 Windows 下导过来的! — —||
也就是说,userid.txt 这个文本的换行符是 Windows 格式,在 Linux 下读取会带有^M。
所以解决上述问题,就很明了了,要么转换 userid.txt 的换行格式,要不就修改代码,去掉多余的字符!
试了下转换格式,发现居然转换不成功,可能是我没找对方法,暂时先不折腾了!
直接如下修改代码,就搞定了:
#!/bin/bash cat -A userid.txt|while read userId; do #利用 cut 命令取出 ^ 之前的数字部分: id=`echo $userId | cut -d"^" -f1` grep $id record.txt done
好了,搞了半天原来是 dos 和 unix 的换行符问题!o(︶︿︶)o 唉!还是经验不足啊!
网上那些问 grep 无法搜索变量的朋友,赶紧看看是不是文本格式造成的!现在,让我很纳闷的是,为毛在另一台 centos 系统可以直接 grep??为什么在 SUSE 系统就不行?
如果和发行版没关系的话,那造成 2 个不同结果的原因就只有一个:在我用 sz+rz 命令将所有文本传送到 centos 的过程中,文件很可能被自动转格式了!好吧,具体就不深究了,有兴趣的可以试试看。
可以下载工具unit2dos!可以转换格式???
多加一个命令dos2unix就可以搞定个问题。
多谢指点,回头补充下。
支持一下
卧槽,我弄了一天就是弄不出来然后靠你这个解决了!简直太感谢了!
在vi里的编辑模式下:set ff=unix也不行吗?