Como posso classificar o conteúdo dos arquivos encontrados usando find em um único arquivo?

11

Consegui me fotografar onde dói (muito ruim) reformatando uma partição que continha dados valiosos. Claro que não foi intencional, mas aconteceu.

No entanto, eu consegui usar testdiske photorecrecuperar a maioria dos dados. Então agora eu tenho todos esses dados distribuídos em quase 25.000 diretórios. A maioria dos arquivos são arquivos .txt, enquanto os demais são arquivos de imagem. Existem mais de 300 arquivos .txt em cada diretório.

Eu posso grepou uso findpara extrair determinadas seqüências de caracteres dos arquivos .txt e enviá-las para um arquivo. Por exemplo, aqui está uma linha que eu usei para verificar se meus dados estão nos arquivos recuperados:

find ./recup*/ -name '*.txt' -print | xargs grep -i "searchPattern"

Eu posso gerar "searchPattern" para um arquivo, mas isso me dá esse padrão. Aqui está o que eu realmente gostaria de realizar:

Percorra todos os arquivos e procure por uma sequência específica. Se essa sequência for encontrada em um arquivo, coloque TODO o conteúdo desse arquivo em um arquivo de saída. Se o padrão for encontrado em mais de um arquivo, anexe o conteúdo dos arquivos subseqüentes ao arquivo de saída. Observe que eu simplesmente não quero exibir o padrão que estou procurando, mas TODO o conteúdo do arquivo no qual os padrões são encontrados.

Eu acho que isso é possível, mas simplesmente não sei como capturar todo o conteúdo de um arquivo depois de receber um padrão específico dele.

Ami
fonte
Portanto, com o comando que você forneceu, ele fornece os resultados que você procura, mas procura redirecionar a saída para um arquivo de texto?
ryekayo
Depois de ler minha pergunta, o parágrafo que começa com "Passar por ..." soa como psuedocode. Talvez eu possa obtê-lo com algumas linhas de código Python for / if. Vai dar-lhe um tiro enquanto aguardo uma resposta mais informada
Ami
Certamente é psuedocode, e tenho certeza de que você também encontrará uma maneira de fazê-lo no bash.
ryekayo
@ryekayo, Sim, isso me dá a saída, mas é apenas para descobrir em qual arquivo um tipo específico de dados está, o que me diz que mais desses dados estão nesse arquivo. Então, eu quero pegar tudo nesse arquivo e gravá-los em outro arquivo.
Ami
Você provavelmente pode quebrar esse comando em algum tipo de instrução if ou até mesmo um caso de interruptor que pode chamar uma função que pode gato para fora o conteúdo baseado no caso ou os resultados da instrução if
ryekayo

Respostas:

10

Se entendi seu objetivo corretamente, o seguinte fará o que você deseja:

find ./recup*/ -name '*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Isso procurará todos os *.txtarquivos ./recup*/, testará cada um searchPattern, se corresponder catao arquivo. A saída de todos os catarquivos ed será direcionada para outputfile.txt.

Repita para cada arquivo de padrão e saída.


Se você tiver um número muito grande de diretórios correspondentes ./recup*, poderá acabar com a argument list too long error. A maneira mais simples de contornar isso é fazer algo assim:

find ./ -mindepth 2 -path './recup*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Isso corresponderá ao caminho completo. Então ./recup01234/foo/bar.txtserá correspondido. O -mindepth 2é para que não corresponda ./recup.txt, ou ./recup0.txt.

Patrick
fonte
Sim, acho que vai fazer isso. E isso me dá uma base para trabalhar. Desde que eu vou procurar por várias strings, acho que um bit for / if de código, com vários elifs, me ajudará a automatizar a tarefa. Obrigado
Ami
Isso é ainda melhor do que o que eu estava pensando lol
ryekayo
Isso não pareceu funcionar. Ocorreu este erro: "não é possível executar / usr / bin / find: a lista de argumentos é muito longa"
Ami
Resposta atualizada da @Ami para fornecer uma solução para esse problema.
Patrick Patrick
2
@Ami Se você estiver usando várias seqüências, pode ser mais simples apenas salvar todos os nomes de arquivos positivos para outro arquivo ( grep -l), em seguida, |sort|uniqe catda lista de arquivos.
Sparhawk 12/09
3

Em vez de emitir seu padrão, imprima o nome do arquivo usando "-l" no grep e use-o como entrada para cat.

find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern" | xargs cat

ou

cat $( find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern")

Eu suspeito que você pode preencher os detalhes restantes. BTW, se você tiver espaços ou outros caracteres estranhos nos nomes dos arquivos (improvável neste caso específico, mas para propósitos futuros), use -print0 na localização e -Z no grep, combinada com a opção -0 no xargs para usar bytes nulos entre nomes de arquivos em vez de novas linhas.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
fonte
2
Também gosto da opção "two -exec" de Patrick, exceto que ela causará um novo fork (bem, clone ()) e exec para cada arquivo. Normalmente você pode usar em \+vez de \;evitar esse problema, mas não sei como isso funciona com um par de -exec args (suspeito "mal"). Usando um par de xargs, você terá apenas alguns processos novos, o que deve ser mais rápido com muitos arquivos.
dannysauer
Parece bom também. Obrigado. Uma pergunta noob: o gato após o último xargs deve estar enviando para um arquivo, certo?
Ami
Quando o li pela primeira vez, não achei que a pergunta especificasse para onde deveria ir o conteúdo do arquivo. Todos os três destes comandos colocar o conteúdo do arquivo (s) em STDOUT, para que acabara de acréscimo (até o fim) >afileou |acommandou o que for apropriado para sua situação. :)
dannysauer
Boa resposta, eu precisava cat pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work
Isso é um pouco fora de tópico, mas prefiro usar em sudo xargsvez de xargs sudo. Quando você executa xargs sudo, ele cria a linha de comando, assumindo que o comando é sudo cat args. Mas o gato está em / bin, então o sudo é executado /bin/cat args. Se o seu comando estiver em um diretório mais longo, como / usr / local / bin, o comando sudo realmente será executado pode resultar em uma linha de comando muito longa e em um erro difícil de rastrear. Além disso, sudo xargsapenas registre o que você executou xargs, enquanto xargs sudoregistra o comando com todos os argumentos - resultando em algumas longas linhas de log do sudo. :)
dannysauer
1

Este não é o código ideal, mas é muito simples e funcionará bem se a eficiência não for um problema. O problema é que ele percorre os arquivos várias vezes, mesmo que a string já tenha sido encontrada neles.

Em primeiro lugar, procure suas strings e escreva os arquivos correspondentes em uma lista.

find ./recup*/ -name '*.txt' -execdir grep -il "searchPattern" {} >> /tmp/file_list \;

Repita esta etapa substituindo searchPatternconforme necessário. Isso produz uma lista de arquivos correspondentes em /tmp/file_list.

O problema é que esse arquivo pode ter duplicatas. Portanto, podemos substituir as duplicatas por |sort|uniq. A sortpeça coloca as duplicatas adjacentes uma à outra, para que uniqpossam ser removidas. Em seguida, você pode catesses arquivos juntos usando xargs(com cada nome de arquivo separado por nova linha \n). Conseqüentemente,

</tmp/file_list sort | uniq | xargs -d "\n" cat > final_file.txt

Ao contrário das outras respostas, ele possui duas etapas e um arquivo temporário; portanto, eu recomendaria apenas se você tiver vários padrões a serem encontrados.

Sparhawk
fonte
0

Dependendo do seu shell e ambiente, você pode fazer algo assim (no bash)

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1\|searchPattern2\|searchPattern3' "$file"; then
    cat "$file" >> some/other/file
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

Se você quiser separar os resultados de acordo com o padrão, poderá modificá-lo para algo como

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1' "$file"; then
    cat "$file" >> some/other/file1
  elif grep -qim1 'searchPattern2' "$file"; then
    cat "$file" >> some/other/file2
  elif grep -qim1 'searchPattern3' "$file"; then
    cat "$file" >> some/other/file3
  fi
done < <(find ./recup*/ -name '*.txt' -print0)
chave de aço
fonte
O que faz o bit depois de "pronto"? O que eu realmente gosto é modificar esse bloco if para que os arquivos que contêm um padrão correspondente sejam gravados em um diferente.
Ami
Ele apenas lista os arquivos '.txt' encontrados, cada um sendo finalizado pelo caractere nulo (para que seja seguro para nomes de arquivos que contenham espaços e outros caracteres). O whileloop então lê essa lista e faz a parte grep/ condicional cat.
steeldriver
Quando tento executar o código, eu recebo este erro: Erro de sintaxe:: ./recoverData.sh "(" inesperado que é proveniente dos suportes em torno do comando find.
Ami
Qual shell você está usando? a sintaxe de substituição processo é específico para o bash - daí a minha qualificação "Dependendo da sua concha e meio ambiente"
steeldriver
1
Você pode executar os comandos diretamente em um shell bash interativo ou colocá-los em um arquivo cuja primeira linha contenha o shebang #!/bin/bash, chmod +x recoverData.shexecutá-los e executá-los usando ./recoverData.sh. Você não usar sh recoverData.shuma vez /bin/shé provável que um dashshell .
steeldriver