
Estou tentando fazer o seguinte:
cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
Espero que cada linha do arquivo1 seja o valor do grep no final do terceiro canal. Não está funcionando como esperado.
Isso ocorre porque -I{}
para de procurar coisas para substituir quando atinge o cano? Existe uma maneira de contornar isso?
Responder1
É porque você precisa de um shell para criar um canal ou executar o redirecionamento. Observe que cat
é o comando para concatenar, não faz muito sentido usá-lo apenas para um arquivo.
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
Fazernãofazer:
gato arquivo1.txt | xargs -I{} sh -c 'cat arquivo2.txt | grep -e {}'
pois isso equivaleria a uma vulnerabilidade de injeção de comando. O {}
seria expandido no argumento code para sh
ser interpretado como código shell. Por exemplo, se uma linha file1.txt
fosse $(reboot)
chamada reboot
.
O -e
(ou você também pode usar --
) também é importante. Sem ele, você teria problemas com expressões regulares começando com -
.
Você pode simplificar o acima usando redirecionamentos 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 grep
em vez de usar redirecionamentos. Nesse caso, você pode até descartar sh
:
< file1.txt xargs -I{} grep -e {} file2.txt
Você também poderia grep
procurar todas as expressões regulares de uma vez em uma única invocação:
grep -f file1.txt file2.txt
Observe, no entanto, que, nesse caso, é apenas um regexp para cada linha de file1.txt
, não há nenhum processamento especial de cotação feito por xargs
.
xargs
por padrão considera sua entrada como uma lista de palavras em branco (com algumas implementações apenas espaço e tabulação, em outras qualquer na [:blank:]
classe de caracteres do código do idioma atual) ou palavras separadas por nova linha para as quais barra invertida e aspas simples e duplas podem ser usadas para escapar dos separadores (a nova linha só pode ser escapada por barra invertida) ou entre si.
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 finais). Os espaços em branco não são mais considerados separadores, mas o processamento de cotações ainda está sendo feito.
Na entrada acima xargs -I{}
passaria um a "b" foo bar x<newline>y
argumento 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. Em suma, xargs -I{}
é bastante inútil.
Se você quiser que cada linha seja passada literalmente como argumento para o comando, você pode usar xargs
-d '\n'
a extensão GNU:
< 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 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 cada umpalavraem file1.txt
(aspas ainda reconhecidas) em oposição a cadalinhaa ser procurado (o que também resolveria seu problema de espaço à direita se você tivesse uma palavra por linha), 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 xargs
), 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
Responder2
Dependendo do que você está tentando fazer, talvez seja melhor pular xargs
completamente e optar por esta solução:
grep -f file1.txt file2.txt
Isso difere do seu comando original(depois de corrigirmos como na resposta de Stéphane Chazelas) da seguinte forma:
- As linhas são impressas na ordem em que aparecem,
file2.txt
independentemente dos padrões com que correspondam. No seu comando, todas as linhas que correspondem ao primeiro padrão são impressas, depois todas as linhas que correspondem ao segundo e assim por diante. - As linhas que correspondem a mais de um padrão são impressas exatamente uma vez. No seu comando, eles são impressos uma vez para cada padrão correspondente.
- Vários sinalizadores podem ser usados mais facilmente, incluindo ambos
-v
e-c
.
A -f
bandeira éespecificado por POSIXe, portanto, razoavelmente portátil.