我正在尝试执行以下操作:
cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
我期望 file1 中的每一行都是第三个管道末尾的 grep 的值。它没有按预期工作。
这是因为-I{}
一旦它到达管道就停止寻找替换的东西吗?有没有解决的办法?
答案1
这是因为您需要一个 shell 来创建管道或执行重定向。请注意,这cat
是连接命令,仅对一个文件使用它没有什么意义。
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
做不是做:
猫文件1.txt | xargs -I{} sh -c 'cat file2.txt | xargs -I{} sh -c 'cat file2.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'
因为这相当于命令注入漏洞。将{}
在代码参数中扩展sh
为 shell 代码。例如,如果 的 一行file1.txt
是$(reboot)
,则将调用reboot
.
(-e
或者您也可以使用--
)也很重要。如果没有它,您就会遇到以 . 开头的正则表达式问题-
。
您可以使用重定向来简化上述内容,而不是cat
:
< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}
或者简单地将文件名作为参数传递给grep
而不是使用重定向,在这种情况下您甚至可以删除sh
:
< file1.txt xargs -I{} grep -e {} file2.txt
您还可以grep
在一次调用中一次性查找所有正则表达式:
grep -f file1.txt file2.txt
但请注意,在这种情况下, 的每一行都只有一个正则表达式file1.txt
, 没有进行任何特殊的引号处理xargs
。
xargs
默认情况下,将其输入视为空白列表(在某些实现中只有空格和制表符,在其他实现中只有[:blank:]
当前语言环境的字符类中的任何字符)或换行符分隔的单词,其中反斜杠以及单引号和双引号可用于转义分隔符(换行符只能通过反斜杠转义)或彼此。
例如,对于这样的输入:
'a "b'\" "bar baz" x\
y
xargs
没有-I{}
将通过a "b"
,bar baz
和x<newline>y
命令。
使用 时-I{}
,xargs
每行获取一个单词,但仍进行一些额外的处理。它忽略前导(但不忽略尾随)空格。空格不再被视为分隔符,但报价处理仍在进行中。
上面的输入将向命令xargs -I{}
传递一个参数。a "b" foo bar x<newline>y
另请注意,根据 POSIX 的要求,如果单词长度超过 255 个字符,许多系统将无法工作。总而言之,xargs -I{}
很没用。
如果您希望将每一行逐字作为参数传递给命令,您可以使用 GNUxargs
-d '\n'
扩展:
< file1.txt xargs -d '\n' -n 1 grep file2.txt -e
(这里依赖于 GNU 的另一个扩展grep
,它允许在参数之后传递选项(前提是环境中不存在 POSIXly 正确的选项)或可移植:
sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
for line do
grep -e "$line" file2.txt
done' sh
如果你想要每一个单词在file1.txt
(引号仍然被识别)而不是每个线要查找(如果每行只有一个单词,这也可以解决尾随空格问题),您可以xargs -n1
单独使用而不是使用-I
:
< file1.txt xargs -n1 sh -c '
for word do
grep -e "$word" file2.txt
done' sh
要去除前导和尾随空白(但没有引号处理xargs
),您还可以执行以下操作:
unset IFS # restore word splitting to its default
while read -r regexp; do
grep -e "$regexp" file2.txt
done < file1.txt
答案2
根据您想要执行的操作,您最好xargs
完全跳过并使用此解决方案:
grep -f file1.txt file2.txt
这与您原来的命令不同(一旦我们按照 Stéphane Chazelas 的回答修复它)如下:
- 线条按照它们出现的顺序打印,
file2.txt
无论它们匹配哪种模式。在您的命令中,将打印与第一个模式匹配的所有行,然后打印与第二个模式匹配的所有行,依此类推。 - 与多个模式匹配的行将只打印一次。在您的命令中,它们会针对每个匹配的模式打印一次。
- 可以更轻松地使用多个标志,包括
-v
和-c
。
国旗-f
是由 POSIX 指定因此相当便携。