我正在尝试执行以下操作:
cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
Run Code Online (Sandbox Code Playgroud)
我期望 file1 中的每一行都是第三个管道末尾的 grep 值。它没有按预期工作。
这是因为-I{}一旦碰到管道就停止寻找要更换的东西吗?有没有解决的办法?
Sté*_*las 15
这是因为您需要一个 shell 来创建管道或执行重定向。请注意,这cat是连接命令,仅将其用于一个文件是没有意义的。
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
Run Code Online (Sandbox Code Playgroud)
千万不能做的:
cat file1.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 {}
Run Code Online (Sandbox Code Playgroud)
或者简单地将文件名作为参数传递给grep而不是使用重定向,在这种情况下,您甚至可以删除sh:
< file1.txt xargs -I{} grep -e {} file2.txt
Run Code Online (Sandbox Code Playgroud)
您还可以告诉grep在一次调用中一次查找所有正则表达式:
grep -f file1.txt file2.txt
Run Code Online (Sandbox Code Playgroud)
但是请注意,在这种情况下,对于 的每一行,这只是一个正则表达式file1.txt,没有由xargs.
xargs默认情况下,将其输入视为空白列表(在某些实现中只有空格和制表符,在其他实现[:blank:]中是当前语言环境的字符类中的任何字符)或换行符分隔的单词,反斜杠和单引号和双引号可用于转义分隔符(换行符只能通过反斜杠转义)或彼此。
例如,在如下输入上:
'a "b'\" "bar baz" x\
y
Run Code Online (Sandbox Code Playgroud)
xargswithout-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
Run Code Online (Sandbox Code Playgroud)
(这里依赖于 GNU 的另一个扩展,grep它允许在参数之后传递选项(假设环境中没有 POSIXly 正确)或可移植:
sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
for line do
grep -e "$line" file2.txt
done' sh
Run Code Online (Sandbox Code Playgroud)
如果你想在每一字中file1.txt,而不是每个(行情仍识别)线被寻找(这也将解决您的尾随空间的问题,如果你有每行反正一个字),你可以使用xargs -n1,而不是单独使用的-I:
< file1.txt xargs -n1 sh -c '
for word do
grep -e "$word" file2.txt
done' sh
Run Code Online (Sandbox Code Playgroud)
要去除前导和尾随空白(但没有引用处理xargs),您还可以执行以下操作:
unset IFS # restore word splitting to its default
while read -r regexp; do
grep -e "$regexp" file2.txt
done < file1.txt
Run Code Online (Sandbox Code Playgroud)
根据您尝试执行的操作,您最好xargs完全跳过,而改用此解决方案:
grep -f file1.txt file2.txt
Run Code Online (Sandbox Code Playgroud)
这与您的原始命令不同(一旦我们按照 Stéphane Chazelas 的回答修复它)如下:
file2.txt而不管它们匹配哪种模式。在您的命令中,将打印与第一个模式匹配的所有行,然后打印与第二个模式匹配的所有行,依此类推。-v和-c。该-f标志由 POSIX 指定,因此具有合理的可移植性。