Como canalizar a lista de arquivos retornados pelo comando find para cat para visualizar todos os arquivos

204

Estou fazendo um finde, em seguida, obtendo uma lista de arquivos. Como canalizá-lo para outro utilitário como cat(para que o gato exiba o conteúdo de todos esses arquivos) e basicamente precise de grepalgo desses arquivos.

Devang Kamdar
fonte

Respostas:

340
  1. Canalização para outro processo (embora isso NÃO realize o que você disse que está tentando fazer):

    command1 | command2
    

    Isso enviará a saída do comando1 como a entrada do comando2

  2. -execem um find(isso fará o que você deseja fazer - mas é específico find)

    find . -name '*.foo' -exec cat {} \;
    

    (Tudo entre finde -execos predicados de localização que você já estava usando. {}Substituirá o arquivo específico que você encontrou no comando ( cat {}nesse caso); \;é para finalizar o -execcomando.)

  3. envia a saída de um processo como argumentos de linha de comando para outro processo

    command2 `command1`
    

    por exemplo:

    cat `find . -name '*.foo' -print`
    

    (Observe que estas não são aspas regulares (sob o til ~ do meu teclado).) Isso enviará a saída de command1para command2argumentos de linha de comando. Observe que os nomes de arquivos que contêm espaços (novas linhas, etc) serão divididos em argumentos separados.

kenj0418
fonte
2
cat find -name '*.foo' -printtrabalhou muito para mim ... Obrigado
Devang Kamdar
As aspas funcionam muito bem e são mais generalizadas; você também pode usá-lo para criar uma lista de arquivos de um arquivo.
Hazok
11
Observe que as versões modernas do findpermitem escrever:, find . -name '*.foo' -exec cat {} +onde +indica que finddeve agrupar quantos nomes de arquivos forem convenientes em uma única chamada de comando. Isso é bastante útil (lida com espaços etc. nos nomes de arquivos sem recorrer a -print0e xargs -0).
22611 Jonathan
18
Unmentioned:find . -name '*.foo' | xargs cat
stewSquared
3
Só para acrescentar em resposta @stewSquared s: Para encontrar todas as linhas em arquivos que contenham uma determinada corda, fazerfind . -name '*.foo' | xargs cat | grep string
Bim
84

Versão moderna

O POSIX 2008 adicionou o +marcador, o findque significa que agora agrupa automaticamente quantos arquivos forem razoáveis ​​em uma única execução de comando, muito parecido com o xargsfaz, mas com várias vantagens:

  1. Você não precisa se preocupar com caracteres estranhos nos nomes dos arquivos.
  2. Você não precisa se preocupar com o comando ser chamado com zero nome de arquivo.

O problema do nome do arquivo é um problema xargssem a -0opção e o problema 'executar mesmo com zero nome de arquivo' é um problema com ou sem a -0opção - mas o GNU xargstem a opção -rou --no-run-if-emptypara impedir que isso aconteça. Além disso, essa notação reduz o número de processos, não é provável que você avalie a diferença de desempenho. Portanto, você poderia escrever sensatamente:

find . -exec grep something {} +

Versão clássica

find . -print | xargs grep something

Se você estiver no Linux ou tiver o GNU finde os xargscomandos, use -print0com finde -0com xargspara manipular nomes de arquivos que contenham espaços e outros caracteres estranhos.

find . -print0 | xargs -0 grep something

Ajustando os resultados de grep

Se você não quiser os nomes dos arquivos (apenas o texto), adicione uma opção apropriada para grep(geralmente -hpara suprimir 'títulos'). Para garantir absolutamente que o nome do arquivo seja impresso grep(mesmo que apenas um arquivo seja encontrado ou a última chamada de greptenha apenas um nome de arquivo), adicione /dev/null-o à xargslinha de comando, para que sempre haja pelo menos dois nomes de arquivo.

Jonathan Leffler
fonte
Para aqueles confusos como eu, observe que dessa maneira, primeiro, será exibida toda a saída de find e, depois, a saída de xargs grep something.
Eric Hu
3
@ EricHu: Eu posso ver que você está confuso, mas não faz o que você diz que faz, pelo menos não em qualquer sistema baseado em Unix que eu conheça. A saída de findé canalizada para a entrada padrão de xargs. O xargsprograma lê sua entrada padrão, dividindo a entrada em espaço em branco (espaços em branco, novas linhas, guias, etc) e anexa várias palavras ao comando grep somethinge executa a linha de comando. xargsem seguida, continua lendo a entrada e executando comandos até ficar sem entrada. xargsexecuta o grepcomando quantas vezes for necessário para a entrada que é fornecida ( findneste exemplo).
Jonathan Leffler
Ah meu erro, isso é usar grep para pesquisar dentro de cada arquivo correspondente. Eu estava olhando para simplesmente filtrar a saída do achado com grep
Eric Hu
1
Os erros vão para o erro padrão (descritor de arquivo 2) em todos os comandos bem comportados. Redirecionar stderr para /dev/nullperde as mensagens de erro.
11136 Jonathan Leffler
1
Isso também tem o benefício de funcionar melhor com espaços no caminho do arquivo. Até 'sed'ing "" -> "\" o quebra com o `mas com xargs ele funciona perfeitamente
JZL003
36

Existem algumas maneiras de passar a lista de arquivos retornados pelo findcomando para o catcomando, embora tecnicamente nem todos usem canalização, e nenhum deles seja canalizado diretamente cat.

  1. O mais simples é usar backticks ( `):

    cat `find [whatever]`
    

    Isso pega a saída finde efetivamente a coloca na linha de comando de cat. Isso não funciona bem se findhouver muita saída (mais do que pode caber em uma linha de comando) ou se a saída tiver caracteres especiais (como espaços).

  2. Em algumas conchas, inclusive bash, pode-se usar em $()vez de reticulares:

    cat $(find [whatever])
    

    Isso é menos portátil, mas é aninhado. Além disso, tem praticamente as mesmas ressalvas que os backticks.

  3. Como a execução de outros comandos no que foi encontrado é de uso comum find, o find possui uma -execação que executa um comando para cada arquivo encontrado:

    find [whatever] -exec cat {} \;
    

    O {}é um espaço reservado para o nome do arquivo e \;marca o final do comando (é possível executar outras ações depois -exec.)

    Isso será executado catuma vez para cada arquivo, em vez de executar uma única instância, cattransmitindo vários nomes de arquivos que podem ser ineficientes e podem não ter o comportamento desejado para alguns comandos (embora seja bom cat). A sintaxe também é difícil de digitar - você precisa escapar do ponto e vírgula porque o ponto e vírgula é especial para o shell!

  4. Algumas versões find(principalmente a versão GNU) permitem substituir ;por+ a utilização -execdo modo de acréscimo para correr menos casos de cat:

    find [whatever] -exec cat {} +
    

    Isso passará vários nomes de arquivos para cada chamada de cat, o que pode ser mais eficiente.

    Observe que isso não garante que você use uma única chamada, no entanto. Se a linha de comando for muito longa, os argumentos serão espalhados por várias invocações decat . Pois catisso provavelmente não é grande coisa, mas para alguns outros comandos isso pode mudar o comportamento de maneiras indesejáveis. Nos sistemas Linux, o limite de comprimento da linha de comando é bastante grande, portanto, a divisão em várias invocações é bastante rara em comparação com outros sistemas operacionais.

  5. A abordagem clássica / portátil é usar xargs:

    find [whatever] | xargs cat
    

    xargsexecuta o comando especificado ( catneste caso) e adiciona argumentos com base no que lê do stdin. Assim como -execcom +, isso vai quebrar a linha de comando, se necessário. Ou seja, se findproduzir muita saída, ele será executado catvárias vezes. Conforme mencionado na seção -execanterior, existem alguns comandos em que essa divisão pode resultar em um comportamento diferente. Observe que usarxargs dessa maneira apresenta problemas com espaços nos nomes de arquivos, pois xargsapenas usa espaços em branco como delimitador.

  6. O método mais robusto, portátil e eficiente também usa xargs :

    find [whatever] -print0 | xargs -0 cat
    

    O -print0sinalizador indica findpara usar \0delimitadores (caracteres nulos) entre os nomes de arquivos, e o -0sinalizador informa xargspara esperar esses \0delimitadores. Isso tem um comportamento praticamente idêntico ao da abordagem -exec... +, embora seja mais portátil (mas infelizmente mais detalhado).

Laurence Gonsalves
fonte
O método backtick é ótimo, porque também funciona para outros comandos ls.
Martin Braun
@Martin Braun usando $()Também funciona com comandos diferentes de find .
Laurence Gonsalves
Obrigado, bom saber, eu parei de ler depois de (1), porque atende às minhas necessidades, já que não estou lidando com caracteres especiais, como espaços e coisas do tipo.
Martin Braun
9

Para conseguir isso (usando o bash), faria o seguinte:

cat $(find . -name '*.foo')

Isso é conhecido como a "substituição de comando" e retira o avanço de linha por padrão, o que é realmente conveniente!

mais informações aqui

Stphane
fonte
6

Parece um trabalho para um script de shell para mim:

for file in 'find -name *.xml'
do
   grep 'hello' file
done

ou algo assim

Gandalf
fonte
2
Esta é uma resposta válida, embora não necessariamente ideal, para a pergunta.
24616 Jonathan Leffler
1
... sim, mas é ótimo se você quiser um arquivo grande com nomes de arquivos listados também.
ʍǝɥʇɐɯ
1
Eu gosto mais disso. Um bloco de loop como esse deixa espaço para outras coisas.
kakyo
4

Aqui está a minha maneira de encontrar nomes de arquivos que contenham algum conteúdo do meu interesse, apenas uma única linha de base que lide com espaços também nos nomes de arquivos:

find . -name \*.xml | while read i; do grep '<?xml' "$i" >/dev/null; [ $? == 0 ] && echo $i; done
Greg
fonte
3

Eu uso algo como isto:

find . -name <filename> -print0 | xargs -0 cat | grep <word2search4>

" -print0" argumento para "localizar" e " -0" argumento para "xargs" são necessários para manipular espaços em branco nos caminhos / nomes de arquivos corretamente.

ekinak
fonte
2

O comando find possui um argumento -exec que você pode usar para coisas como essa; você pode fazer o grep diretamente usando isso.

Por exemplo ( daqui, outros bons exemplos nesta página ):

find . -exec grep "www.athabasca" '{}' \; -print 
Chad Birch
fonte
2

Aqui está minha foto para uso geral:

grep YOURSTRING `find .`

Irá imprimir o nome do arquivo

zakki
fonte
2

No bash, o seguinte seria apropriado:

find /dir -type f -print0 | xargs -0i cat {} | grep whatever

Isso localizará todos os arquivos no /dirdiretório e canalizará com segurança os nomes de arquivos xargs, os quais serão conduzidos com segurança grep.

Ignorar xargsnão é uma boa ideia se você tiver muitos milhares de arquivos /dir; catserá interrompido devido ao tamanho excessivo da lista de argumentos. xargsvai resolver tudo isso para você.

O -print0argumento findmescla com o -0argumento xargspara manipular nomes de arquivos com espaços corretamente. O -iargumento para xargspermite inserir o nome do arquivo quando necessário na catlinha de comando. Os colchetes são substituídos pelo nome do arquivo canalizado no catcomando de find.

McClain Looney
fonte
0

Isso funciona para mim

find _CACHE_* | while read line; do
    cat "$line" | grep "something"
done
Steven Penny
fonte
0

Use ggrep .

ggrep -H -R -I "mysearchstring" *

para procurar um arquivo no unix contendo texto localizado no diretório atual ou em um subdiretório

Underverse
fonte
0

Isso imprimirá o nome e o conteúdo dos arquivos somente recursivamente.

find . -type f -printf '\n\n%p:\n' -exec cat {} \;

Editar (versão aprimorada): imprime o nome e o conteúdo dos arquivos de texto (ascii) somente recursivamente.

find . -type f -exec grep -Iq . {} \; -print | xargs awk 'FNR==1{print FILENAME ":" $0; }'

Mais uma tentativa

find . -type f -exec grep -Iq . {} \; -printf "\n%p:" -exec cat {} \;
Prashant Adlinge
fonte
-1

Você está tentando encontrar texto nos arquivos? Você pode simplesmente usar grep para isso ...

grep searchterm *
Scott Anderson
fonte
-1

Para listar e ver o conteúdo de todos os arquivos abc.def em um servidor nos diretórios / ghi e / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec ls {} \; -exec cat {} \;

Para listar os arquivos abc.def que comentaram entradas e exibem, consulte essas entradas nos diretórios / ghi e / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec grep -H ^# {} \;
Sharjeel
fonte