É 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