file grep dall'elenco


14

Sto cercando di eseguire grep su un elenco di alcune centinaia di file:

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

Tuttavia, anche se sto cercando una stringa che so essere trovata nei file, quanto segue non cerca i file:

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

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

Conosco la -fbandiera che leggerà i motivi da un file. Ma come leggere i file di input ?

Avevo preso in considerazione l'orribile soluzione alternativa di copiare i file in una directory temporanea in quanto cpsembra supportare il <(cat files.txt)formato e da lì inoltro dei file. Shirley c'è un modo migliore.

Risposte:


22

Sembra che stai inserendo l'elenco dei nomi dei file, non dei file stessi. <(cat files.txt)elenca solo i file. Prova <(cat $(cat files.txt))a concatenarli effettivamente e cercali come un singolo flusso, oppure

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

per dare a grep tutti i file.

Tuttavia, se l'elenco contiene troppi file, è possibile che si verifichino problemi con il numero di argomenti. In quel caso avrei solo scritto

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt

Grazie! Non mi rendevo conto che whilepoteva ricevere le righe di file.txt in quanto tale.
dotancohen,

Ti consigliamo di disabilitare la parte glob di quell'operatore split + glob qui (a meno che la shell non sia zsh).
Stéphane Chazelas,

1
whilenon sta esattamente ricevendo le righe dal file, lo readsta facendo; whileci permette di farlo in un ciclo. Il ciclo termina quando readfallisce (cioè restituisce un codice di ritorno diverso da zero), normalmente a causa del raggiungimento del fine del file.
PM 2Ring

1
Leggere una riga (di testo), la sintassi è IFS= read -r filename, read filenameè qualcos'altro.
Stéphane Chazelas,

1
Nota che -Hè un'estensione GNU. Ne manchi un po ' --.
Stéphane Chazelas,

8
xargs grep -i -- foo /dev/null < files.txt

supponendo che i file siano vuoti o delimitati da newline (dove è possibile usare virgolette o barre rovesciate per sfuggire a quei separatori). Con GNU xargsè possibile specificare il delimitatore con -d(che quindi disabilita la gestione dei preventivi).

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

supponendo che i file siano separati da spazio, tabulazione o newline (non c'è modo di sfuggire a quelli sebbene sia possibile scegliere un separatore diverso assegnandolo a IFS). Quello fallirà se l'elenco dei file è troppo grande sulla maggior parte dei sistemi.

Quelli presumono anche che nessuno dei file sia chiamato -.


È meglio / più veloce da usare $(< file)invece di $(cat file), almeno in bashe zsh.
Jimmij,

7

Per leggere un elenco di nomi di file da stdin è possibile utilizzare xargs. Per esempio,

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

Per impostazione predefinita, xargslegge gli elementi dall'input standard, delimitato da spazi. Gli -d'\n'dice di usare newline come delimitatore di argomenti, quindi può gestire nomi di file contenenti spazi. (Come sottolinea Stéphane Chazelas, questa è un'estensione GNU). Tuttavia, non affronterà i nomi dei file contenenti newline; avremmo bisogno di un approccio leggermente più complicato per gestirli.

FWIW, questo approccio è un po 'più veloce di un while readciclo, poiché il readcomando di bash è molto lento: legge i suoi dati carattere per carattere, mentre xargslegge il suo input in modo più efficiente. Inoltre, xargsinvoca il grepcomando solo tutte le volte che è necessario, con ogni invocazione che riceve più nomi di file, ed è più efficiente che invocare grepsingolarmente per ciascun nome di file.

Vedi la pagina man di xargs e la pagina di informazioni di xargs per ulteriori dettagli.


3

xargspuò leggere elementi da un file (come il tuo files.txtelenco) con la sua opzione:

   --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.

Quindi dovrebbe funzionare anche questo:

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

o per spazi nei nomi di file

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}

1

Puoi anche fare un for ma l'esempio di Orion è il più semplice:

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

(Per ogni file elencato in files.txt eseguire il comando grep su di esso.)

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.