Xargs no segundo lado do tubo?

Xargs no segundo lado do tubo?

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 shser interpretado como código shell. Por exemplo, se uma linha file1.txtfosse $(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 grepem vez de usar redirecionamentos. Nesse caso, você pode até descartar sh:

< file1.txt xargs -I{} grep -e {} file2.txt

Você também poderia grepprocurar 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.

xargspor 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

xargssem -I{}passaria a "b", bar baze x<newline>ypara o comando.

Com -I{}, xargsobté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>yargumento 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 grepque 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 -n1sozinho 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 xargscompletamente 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.txtindependentemente 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 -ve -c.

A -fbandeira éespecificado por POSIXe, portanto, razoavelmente portátil.

informação relacionada