使用Grep结合Xargs与进程替换时的单行搜索异常问题
我太懂你这个头疼的问题了——用xargs传递grep的查询条件,同时靠进程替换(比如<(cat target.txt))指定要搜索的文件,结果grep只扫了一行就停了,完全没遍历整个文件。其实这个坑的核心原因,和进程替换的工作方式脱不了干系。
为啥会出现这种情况?
举个你可能在用的命令例子:
echo -e "foo\nbar" | xargs grep <(cat bigfile.txt)
这里xargs会把foo、bar依次传给grep,但<(cat bigfile.txt)本质是个临时管道。第一个grep进程(处理foo)会把这个管道里的全部内容读完,等第二个grep进程(处理bar)启动时,管道已经空了,自然搜不到东西,看起来就像grep只处理了一行。
几个靠谱的解决办法
1. 直接传文件名,别用进程替换(最推荐)
如果你的目标是实体文件,真没必要绕进程替换,直接把文件名丢给grep就行。配合xargs的-I选项,让每个查询条件都对应一次完整的文件扫描:
echo -e "foo\nbar" | xargs -I {} grep {} bigfile.txt
-I {}会让xargs把每个查询条件替换到{}的位置,这样每个grep进程都会重新打开bigfile.txt,自然能遍历整个文件内容。
2. 用grep的-f选项读查询条件(效率更高)
如果你的查询条件是从管道来的,不如直接用grep自带的-f选项,从标准输入读查询规则,一次性扫完整个文件:
echo -e "foo\nbar" | grep -f - bigfile.txt
这里-f -表示“从标准输入读取查询条件”,grep会一次性遍历bigfile.txt,找出所有匹配任意条件的行,比xargs多次调用grep效率高多了。
3. 逼xargs每次都重新生成进程替换(实在要用进程替换时)
如果因为特殊需求必须用进程替换,那可以用sh -c把命令包起来,让xargs每次调用grep时都重新创建进程替换的管道:
echo -e "foo\nbar" | xargs -I {} sh -c 'grep {} <(cat bigfile.txt)'
这样每个sh -c都会重新执行<(cat bigfile.txt),每个grep都能拿到完整的文件内容。
来测试验证一下
你可以用下面的测试步骤确认效果:
- 先造两个测试文件:
echo -e "line1: foo\nline2: bar\nline3: foo\nline4: baz" > test.txt echo -e "foo\nbar" > patterns.txt
- 用方案1跑命令:
cat patterns.txt | xargs -I {} grep {} test.txt
正常应该输出所有匹配的行:
line1: foo line3: foo line2: bar
这样就实现你要的全文件搜索效果啦。
内容的提问来源于stack exchange,提问作者Luke Anderson- Trocme

