arquivos grep da lista

10

Estou tentando executar o grep em uma lista de algumas centenas de arquivos:

$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php

No entanto, embora eu esteja procurando por uma string que eu sei que é encontrada nos arquivos, o seguinte não pesquisa os arquivos:

$ grep -i 'foo' <(cat files.txt)

$ grep -i 'foo' admin.php
The foo was found

Estou familiarizado com o -f flag que lerá os padrões de um arquivo. Mas como ler os arquivos de entrada ?

Eu considerei a solução horrível de copiar os arquivos para um diretório temporário, pois cp parece suportar o formato <(cat files.txt) e, a partir daí, copiar os arquivos. Shirley, há uma maneira melhor.

    
por dotancohen 13.01.2015 / 10:04

5 respostas

18

Você parece estar exibindo a lista de nomes de arquivos, não os arquivos em si. <(cat files.txt) apenas lista os arquivos. Tente <(cat $(cat files.txt)) para realmente concatená-los e pesquisá-los como um único fluxo, ou

grep -i 'foo' $(cat files.txt)

para dar grep todos os arquivos.

No entanto, se houver muitos arquivos na lista, você poderá ter problemas com o número de argumentos. Nesse caso, eu acabei de escrever

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt
    
por 13.01.2015 / 10:12
8
xargs grep -i -- foo /dev/null < files.txt

assumindo que os arquivos estejam em branco ou delimitados por nova linha (onde aspas ou contrabarras podem ser usadas para escapar desses separadores). Com o GNU xargs , você pode especificar o delimitador com -d (que, no entanto, desativa a manipulação de cotações).

(unset -v IFS; set -f; grep -i -- foo $(cat files.txt))

assumindo que os arquivos sejam separados por espaço, tabulação ou nova linha (não é possível escapar deles, embora seja possível escolher um separador diferente atribuindo-o a IFS ). Esse falhará se a lista de arquivos for muito grande na maioria dos sistemas.

Eles também assumem que nenhum dos arquivos são chamados de - .

    
por 13.01.2015 / 10:56
7

Para ler uma lista de nomes de arquivos de stdin, você pode usar xargs . Por exemplo,

cat files.txt | xargs -d'\n' grep -i -- 'foo'

Por padrão, xargs lê itens da entrada padrão, delimitados por espaços em branco. O -d'\n' diz para usar a nova linha como delimitador de argumentos, para que possa manipular nomes de arquivos contendo espaços em branco. (Como Stéphane Chazelas aponta, essa é uma extensão do GNU). No entanto, ele não lidará com nomes de arquivos contendo novas linhas; precisaríamos de uma abordagem um pouco mais complicada para lidar com isso.

FWIW, essa abordagem é um pouco mais rápida que um loop while read , pois o comando read do bash é muito lento - ele lê seus dados caractere por caractere, enquanto xargs lê sua entrada com mais eficiência. Além disso, xargs invoca apenas o comando grep quantas vezes forem necessárias, com cada chamada recebendo vários nomes de arquivos, e isso é mais eficiente do que invocar grep individualmente para cada nome de arquivo.

Consulte a página de manual xargs e a página de informações xargs para mais detalhes.

    
por 13.01.2015 / 10:56
3

xargs pode ler itens de um arquivo (como sua files.txt list) com sua opção:

   --arg-file=file
   -a file
          Read items from file instead of standard input.  If you use this
          option, stdin remains unchanged when commands are  run.   Other‐
          wise, stdin is redirected from /dev/null.

Então, isso deve funcionar também:

xargs -a files.txt grep -i 'foo'

ou para espaços em nomes de arquivos

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}
    
por 13.01.2015 / 13:36
1

Você também pode fazer um, mas o exemplo de Orion é o mais simples:

for i in $(cat files.txt); do grep -i 'foo' $i ; done

(Para cada arquivo listado no files.txt, execute o comando grep nele).

    
por 13.01.2015 / 17:07