Come posso catalogare il contenuto dei file trovati usando find in un singolo file?


11

Sono riuscito a spararmi dove fa male (davvero male) riformattando una partizione che conteneva dati preziosi. Ovviamente non era intenzionale, ma è successo.

Tuttavia, sono riuscito a utilizzare testdiske photorecrecuperare la maggior parte dei dati. Quindi ora ho tutti quei dati distribuiti in quasi 25.000 directory. La maggior parte dei file sono file .txt, mentre il resto sono file di immagine. Esistono più di 300 file .txt in ciascuna directory.

Posso grepo usare findper estrarre determinate stringhe dai file .txt e inviarle in un file. Ad esempio, ecco una riga che ho usato per verificare che i miei dati siano nei file recuperati:

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

Posso generare "searchPattern" in un file, ma questo mi dà solo quel modello. Ecco cosa mi piacerebbe davvero realizzare:

Esamina tutti i file e cerca una stringa specifica. Se quella stringa viene trovata in un file, cat TUTTI i contenuti di quel file in un file di output. Se il modello si trova in più di un file, aggiungere il contenuto dei file successivi a quel file di output. Si noti che non voglio semplicemente generare lo schema che sto cercando, ma TUTTI i contenuti del file in cui si trovano gli schemi.

Penso che questo sia fattibile, ma non so come afferrare tutto il contenuto di un file dopo aver estratto un modello specifico da esso.


Quindi, con il comando che hai fornito, ti dà i risultati che stai cercando ma stai cercando di reindirizzare l'output su un file di testo?
Ryekayo,

Dopo aver letto la mia domanda, quel paragrafo che inizia con "Passa attraverso ..." suona proprio come psuedocode. Forse posso ottenere il codice con alcune righe di for / if Python code. Ci proverò mentre aspetto una risposta più informata
Ami,

Certamente è psuedocode e sono sicuro che puoi trovare un modo per farlo anche in bash.
Ryekayo,

@ryekayo, Sì, mi dà l'output, ma è solo per trovare in quale file si trova un tipo specifico di dati, il che mi dice che più di quei dati si trovano in quel file. Quindi voglio prendere tutto in quel file e scriverlo in un altro file.
Ami,

Probabilmente puoi racchiudere quel comando in una specie di istruzione if o anche in un caso switch che può chiamare una funzione che può
estrapolare

Risposte:


10

Se capisco correttamente il tuo obiettivo, il seguente farà ciò che desideri:

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

Questo cercherà tutti i *.txtfile ./recup*/, testali per ciascuno searchPattern, se corrisponde catal file. Verrà indirizzato l'output di tutti i catfile ed outputfile.txt.

Ripetere l'operazione per ogni modello e file di output.


Se hai un numero molto grande di directory corrispondenti ./recup*, potresti finire con a argument list too long error. Il modo semplice per aggirare questo è invece fare qualcosa del genere:

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

Questo corrisponderà al percorso completo. Quindi ./recup01234/foo/bar.txtsarà abbinato. È in -mindepth 2modo che non corrisponda ./recup.txt, o ./recup0.txt.


Sì, penso che lo farà. E mi dà una base su cui lavorare. Dal momento che cercherò più stringhe, penso che un bit for / if di codice, con più elif, mi aiuterà ad automatizzare l'attività. Grazie
Ami,

È ancora meglio di quello che stavo pensando lol
ryekayo,

Non sembra funzionare. Ottenuto questo errore: "impossibile eseguire / usr / bin / find: elenco degli argomenti troppo lungo"
Ami,

@Ami ha aggiornato la risposta per fornire una soluzione a quel problema.
Patrick,

2
@Ami Se si utilizzano più stringhe, potrebbe essere più semplice salvare tutti i nomi di file positivi in ​​un altro file ( grep -l), quindi |sort|uniqe catdall'elenco dei file.
Sparhawk,

3

Invece di emettere il tuo modello, genera il nome del file usando "-l" su grep, quindi usalo come input per cat.

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

o

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

Ho il sospetto che tu possa inserire i dettagli rimanenti. A proposito, se potresti avere spazi o altri caratteri dispari nei nomi dei file (improbabile in questo caso specifico, ma per scopi futuri), usa -print0 sulla ricerca e -Z su grep, combinato con l'opzione -0 su xargs da usare byte nulli tra nomi di file anziché newline.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat

2
Mi piace anche l'opzione "two -exec" di Patrick, tranne per il fatto che causerà un nuovo fork (beh, clone ()) ed exec per ogni file. Normalmente puoi usare \+piuttosto che \;evitare quel problema, ma non so come funzioni con una coppia di argomenti -exec (sospetto "male"). Usando una coppia di xargs, avrai solo un paio di nuovi processi generati, che dovrebbero essere più veloci con molti file.
dannysauer,

Anche questo sembra buono. Grazie. Una domanda noob: il gatto dopo gli ultimi xarg dovrebbe essere in uscita su un file, giusto?
Ami,

Quando l'ho letto per la prima volta, non pensavo che la domanda specificasse dove doveva andare il contenuto del file. Tutti e tre questi comandi mettere il contenuto del file (s) su STDOUT, in modo che ci basta aggiungere (fino alla fine) >afileoppure |acommando tutto ciò che è appropriato per la situazione. :)
dannysauer,

Buona risposta, avevo bisogno di cat pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work

Questo è un po 'fuori tema, ma preferisco usare sudo xargsinvece di xargs sudo. Quando si esegue xargs sudo, crea la riga di comando presupponendo che sia il comando sudo cat args. Ma cat è in / bin, quindi sudo viene eseguito /bin/cat args. Se il comando si trova in una directory più lunga, come / usr / local / bin, il comando sudo effettivamente eseguito potrebbe comportare una riga di comando troppo lunga e un errore che è difficile da rintracciare. Inoltre, sudo xargsregistra semplicemente che hai eseguito xargs, mentre xargs sudoregistra il comando con tutti gli argomenti, risultando in alcune lunghe righe di registro sudo. :)
dannysauer,

1

Questo non è esattamente il codice ottimale, ma è molto semplice e funzionerà bene se l'efficienza non è un problema. Il problema è che scorrerà più volte i file, anche se la stringa è già stata trovata in essi.

Innanzitutto, cerca le tue stringhe e scrivi i file corrispondenti in un elenco.

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

Ripetere questo passaggio sostituendo searchPatternse necessario. Questo produce un elenco di file corrispondenti su /tmp/file_list.

Il problema è che questo file potrebbe contenere duplicati. Quindi, possiamo sostituire i duplicati con |sort|uniq. La sortparte posiziona i duplicati uno accanto all'altro, in modo che uniqpossano rimuoverli. Quindi puoi catquesti file insieme usando xargs(con ogni nome di file separato da newline \n). Quindi,

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

A differenza delle altre risposte, ci sono due passaggi e un file temporaneo, quindi lo consiglio davvero solo se hai più schemi da trovare.


0

A seconda della shell e dell'ambiente, potresti fare qualcosa del genere (in 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 si desidera separare i risultati in base al modello, è possibile modificarlo in qualcosa di simile

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)

Cosa fa il bit dopo "fatto"? Quello che mi piace in realtà è modificarlo in blocco in modo che i file che contengono un modello abbinato vengano scritti in un altro.
Ami,

Elenca solo i file '.txt' che vengono trovati, ognuno terminato dal carattere null (in modo che sia sicuro per i nomi di file contenenti spazi e altri caratteri). Il whileciclo quindi legge quell'elenco e fa la parte grep/ condizionale cat.
Steeldriver,

Quando provo a eseguire il codice, ottengo questo errore: ./recoverData.sh: errore di sintassi: "(" imprevisto. Viene dalle parentesi attorno al comando find
Ami,

Che shell stai usando? la sintassi di sostituzione del processo è specifica di bash - quindi la mia qualifica "Dipende dalla tua shell e dall'ambiente"
steeldriver

1
È possibile eseguire i comandi direttamente in una shell bash interattiva oppure inserirli in un file la cui prima riga contiene lo shebang #!/bin/bash, renderlo eseguibile chmod +x recoverData.shed eseguirlo usando ./recoverData.sh. Do Non utilizzare sh recoverData.shin quanto /bin/shè probabile che una dashshell .
Steeldriver,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.