É porque você precisa de um shell para criar um canal ou executar um redirecionamento. Note que cat é o comando para concatenar, faz pouco sentido usá-lo apenas para um arquivo.
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
Não faça :
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'
como isso equivaleria a uma vulnerabilidade de injeção de comando. O {} seria expandido no argumento code para sh , assim interpretado como código shell. Por exemplo, se a linha de file1.txt for $(reboot) , isso chamaria reboot .
O -e (ou você também pode usar -- ) também é importante. Sem isso, você teria problemas com regexps começando com - .
Você pode simplificar os redirecionamentos acima usando, em vez de cat :
< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}
Ou simplesmente passe os nomes dos arquivos como argumento para grep em vez de usar redirecionamentos. Nesse caso, você pode até mesmo descartar sh :
< file1.txt xargs -I{} grep -e {} file2.txt
Você também pode dizer a grep para procurar todos os regexps de uma única vez em uma única chamada:
grep -f file1.txt file2.txt
Note, no entanto, que nesse caso, isso é apenas uma expressão regular para cada linha de file1.txt , não há nenhum processamento especial de citações feito por xargs .
xargs , por padrão, considera sua entrada como uma lista de espaços em branco (com algumas implementações apenas espaço e tabulação, em outros, qualquer na classe de caractere [:blank:] da localidade atual) ou palavras separadas de nova linha para as quais barra invertida e aspas duplas podem ser usadas para escapar dos separadores (a nova linha só pode ser escapada pela barra invertida) ou um ao outro.
Por exemplo, em uma entrada como:
'a "b'\" "bar baz" x\
y
xargs sem -I{} passaria a "b" , bar baz e x<newline>y para o comando.
Com -I{} , xargs obtém uma palavra por linha, mas ainda faz algum processamento extra. Ele ignora os espaços em branco iniciais (mas não à direita). Blanks não são mais considerados separadores, mas o processamento de cotações ainda está sendo feito.
Na entrada acima, xargs -I{} passaria um argumento a "b" foo bar x<newline>y para o comando. Observe também que muitos sistemas, conforme exigido pelo POSIX, não funcionarão se as palavras tiverem mais de 255 caracteres. Apesar de tudo, xargs -I{} é bastante inútil.
Se você quiser que cada linha seja passada verbatim como argumento para o comando, você pode usar o GNU xargs -d '\n' extension:
< file1.txt xargs -d '\n' -n 1 grep file2.txt -e
(aqui contando com outra extensão do GNU grep que permite passar opções após argumentos (desde que o POSIXly correto não esteja no ambiente) ou portável:
sed "s/'/'\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
for line do
grep -e "$line" file2.txt
done' sh
Se você quisesse que cada palavra em file1.txt (citações ainda reconhecidas) em oposição a cada linha fosse procurada (o que também funcionaria em torno do seu problema de espaço à direita se você tiver uma palavra por linha de qualquer maneira), você pode usar xargs -n1 sozinho em vez de usar -I :
< file1.txt xargs -n1 sh -c '
for word do
grep -e "$word" file2.txt
done' sh
Para remover espaços em branco iniciais e finais (mas sem o processamento de cotações que xargs faz), você também pode fazer:
unset IFS # restore word splitting to its default
while read -r regexp; do
grep -e "$regexp" file2.txt
done < file1.txt