Newsgroup: it.comp.os.linux.iniziare
Titolo: Ricerca di tutti i file che contengono due o piu parole

Ciao
mi serve cercare tutti i file che contengono ad esempio le parole
pippo e pluto
non necessarimente sulla stessa linea....
qualche idea ?
grazie

paolob wrote:

find $DIR -type f -exec grep -l -Z pippo {} + | xargs -0 grep -l pluto

paolob wrote:

Fai uno scriptino come questo:
#!/bin/bash
# script.sh
while [ $# -gt 0 ]; do
  awk '/pippo/{a++}
       /pluto/{a++}
       a>=2{print FILENAME;exit}' "$1"
  shift
done
e, a seconda di dove sono i tuoi file, lo esegui con
script.sh *
oppure 
find /base/dir -type f -exec script.sh {} +
Purtroppo non e' super efficiente perche' deve pur sempre eseguire un 
processo awk per ogni file. Con GNU awk, per fortuna questo si puo' evitare. 
Cambia lo script cosi':
#!/bin/bash
# script.sh
awk '/pippo/{a++}
     /pluto/{a++}
     a>=2{print FILENAME;a=0;nextfile}' "$@"
e lo esegui poi allo stesso modo.
Se i file non sono molto grandi puoi anche usare questo al posto di 
script.sh:
perl -n0e 'print "$ARGV\n" if /^(?=.*pippo)(?=.*pluto)/'

      a>=2{print FILENAME;a=0;nextfile}' "$@"
In realta' questo e' sbagliato perche' verrebbe stampato anche un file che 
contiene due volte pippo o due volte pluto. Inoltre c'e' un altro errore 
logico di non-reinizializzazione delle variabili. Quindi questa e' la 
versione corretta:
#!/bin/bash
 
# script.sh
awk 'FNR==1{a=b=0}
     /pippo/{a++}
     /pluto/{b++}
     a && b {print FILENAME;nextfile}' "$@"

      a && b {print FILENAME;nextfile}' "$@"
È migliorabile:
#! /bin/bash
gawk '
 function mynext()
            { printf FILENAME
              a=b=0
              nextfile 
           }
  BEGIN {a=b=0}
  /pippo/ {a=1 ; if (b==1) mynext() }
  /pluto/ {b=1 ;  if (a==1) mynext() } '  "$@"
PS a e b sono dei flag e non contatori
Attento alla shabang! Parecchi sistemi richiedono lo spazio
tra la barra ed il punto interrogativo.

 ...
Che poi, data l'ora tarda, mi è sfuggita la cosa più importante:
fai uno script awk dentro uno script bash?
#! /bin/gawk -f
function mynext() {
    print FILENAME
    a=0


 find $DIR -type f -exec grep -l -Z pippo {} + | xargs -0 grep -l pluto
  Ne approfitto per una domanda veloce. Per fare una roba tipo:
find ./ -name qualcosa -exec cat {} XXX grep qualcosaltro \;
  come devo sistemare il comando nella parte XXX? Voglio cioe', *dentro*
find, concatenare il cat col grep. Il '+ |' che hai usato tu non mi
funziona (almeno credo). :-o
  Solo per capire, eh? :-)
  Grazie e un saluto,
-- 
Gianfranco Bertozzi
Linux User #183585

 tra la barra ed il punto interrogativo.
Mai visto uno, ma ti credo.

 fai uno script awk dentro uno script bash?
Giusta osservazione.


puoi usare l'opzioen -regex di find
se cerchi su tutto l'hard disk devi usare sudo
$ sudo find / -regex ".*pippo.*" -regex ".*pluto.*"

 superenalotto sia superiore, ...
Il problema è che bisogna abituarsi a programmare *SEMPRE* in 
modo pulito e guardingo . Questo bug è difficilissimo da scoprire 
ma molto molto facile da prevenire. 

 due espressioni regolari.
"Forse" ti e' sfuggito che a e b non vengono resettate al termine di ogni 
file.
Prova ad eseguirlo su questi due file:
file1:
abc
pippo
def
file2:
foo
pluto
bar
E vedrai che file2 viene stampato.

 ma molto molto facile da prevenire.
Sono d'accordo riguardo alla programmazione pulita e guardinga, ma in questo 
caso mi pare che non ci sia da preoccuparsi. E anzi, la versione con a++ e 
b++ e' migliore nel caso in cui uno voglie estendere lo script ad esempio 
per cercare i file che contengono 3 pippo e 2 pluto (con qualche 
semplificazione).

 file.
Ah, e "forse" ti e' anche sfuggito che 
printf FILENAME 
e' FAIL.

   Il '+ |' che hai usato tu non mi funziona (almeno credo). :-o
Se la tua domanda riguarda capire perché il find che io ho 
scritto non funziona sul tuo sistema la risposta è: non lo so.
Quale errore hai? Fare un copia & incolla è troppo difficile?
Probabilmente hai bisogno di mettere degli escape; prova 
a scrivere:
find $DIR -type f -exec grep -l -Z pippo '{}' \+ | xargs -0 grep -l pluto
Inoltre il '+' al posto di ';' è una estensione della GNU. 
il '+' fa si che find non avvii grep per ogni file che 
corrisponde ma aspetta prima di trovare un certo numero di 
file in modo che grep lavori più efficientemente su molti 
file. Non tutte le varie implementazioni di find lo supportano.

 qualche semplificazione).
Su questo argomento potremmo fare una discussione interessante 
e costruttiva ma non credo valga la pena di farla per uno script 
così banale. Questa è il classico caso dove la migliore strategia è la 
strategia dello struzzo. voglio dire, io ho impiegato un minuto a scrivere
la mia soluzione con grep e find, e penso altrettanto tu per scrivere la
versione con AWK; vale la pena perdere una giornata per ottimizzarle? 

 ... 
Mi hai beccato, Ho interrotto la fase di debug troppo presto.

> find ./ -name qualcosa -exec cat {} XXX grep qualcosaltro \;
io di solito faccio cosi':
$ find ./ -name qualcosa | while read nomefile; do cat $nomefile xxxx | 
grep qualcos'altro; done
dentro find, col solo exec, non puoi fare un granche'... specie non puoi 
"pipizzare" niente
invece, se passi l'uscita di find ad un ciclo while, a quel punto li 
dentro ci fai quello che vuoi.

 writes:

il manuale di find, a proposito di -regex, dice
  -regex pattern
     File name matches regular expression pattern.
e non "File content matches regular expression pattern."
-- 
Per essere sicura di una notizia per� bisogna sempre leggere le
visioni di entrambe le parti.       --- B'elanna Torres, in IHC

 Inoltre il '+' al posto di ';' è una estensione della GNU. 
  Io pensavo che invece fosse un comando per dirgli di "concatenare" la
pipe, per far proprio quel che volevo io. Mi son sbagliato. :-o
  Se lo reputi utile, posso aprire un thread apposta. Ma dovresti pure
aiutarmi a trovare un subject decente. :-)
  Un saluto e grazie,


 capire meglio.
Puoi fare cosi':
find . -type f -name '*.txt' -exec sh -c 'cat "$1" | grep pippo' sh {} \;
Forse risolve il tuo dubbio, ma in questo caso puoi (e dovresti) benissimo 
fare 
-exec grep pippo {} \;
come hai giustamente supposto. Poi per ottimizzare un po' puoi fare
-exec grep pippo {} +
per eseguire grep su tanti file in un colpo solo, che invece con l'altro 
metodo diventa piu' complicato da realizzare.

 -exec grep pippo {} +
  Questo '+' non lo conoscevo. Non e' che find lo usi molto. Grazie a
tutti per le info (e per la pazienza). 8-)
  Un saluto,


   Immagino che quel "$1"fosse in realta' {}
No, quel "$1" e' in realta'... "$1".
Vedi man bash, opzione -c. E' proprio nella primissima pagina del man.

 meritare un thread apposta. :-)
Bah, le tue domande sono correlate alla questione e ne approfondiscono 
un  aspetto quindi sono gradite. Però se fai una domanda specifica non
puoi che ottenere la risposta che ti ho dato. Tieni presente che qui in 
Italia facciamo le cose all'italiana; le stesse tue domande in altri gruppi 
sono a malapena tollerate e su altri gruppi avrebbero scatenato risposte 
contrariate e flame.
Per il resto vedo che già ti hanno risposto 

 No, quel "$1" e' in realta'... "$1".
  Errore mio. Li' per li' non mi andava ed ho pensato ad una svista tua.
Invece era mia: m'ero piazzato in un posto privo di *.txt. Va anche con
{}, a mia parziale discolpa. :')
  Ora devo solo capire a che serve quel "sh {}" finale. :-)
  Grazie a tutti,


 Per il resto vedo che già ti hanno risposto 
  Si infatti. E suppergiu' ho anche capito. =8-)
  Un saluto e grazie di tutto,


 
vero.
e infatti ogni comando si occupa solo di una cosa specifica, e find si 
occupa solo dei nomi dei file, non del loro contenuto

 find . -type f -name '*.txt' -exec sh -c 'cat "$1" | grep pippo' sh {} \;
avviare per ogni file trovato una shell, che a sua volta avviera' i due 
processi cat  e grep... non credo proprio sia una buona soluzione.
imho, meglio di tutto e' entrare in un ciclo while
$ find . -type f -name "*.txt" | while read; do ...; done

   Si infatti. E suppergiu' ho anche capito. =8-)
vediti la soluzione col ciclo while
non perche' l'ho detta io... ma io la uso spesso, e funziona facile.
poi fai tu.
ciao

 ...
Non farti troppe seghe mentali, Non hai fatto niente di contrario alla netiquette.

 freni a disco... e sei sempre IT
Concordo con i freni a disco però sono convinto che l'altro argomento 
avrebbe moltissimo successo qui su icoli.

Antonio Macchi wrote:

Era solo per rispondere alla domanda "come mettere una pipe con -exec".
Comunque all'interno del while farai pur sempre cat | grep, quindi il numero 
di cat e grep avviati e' lo stesso. Inoltre col while hai problemi a gestire 
i file con caratteri strani nei nomi (\n su tutti, anche se e' raro). Per 
avviare meno processi possibile, come gia' detto, si fa questo:
find . -type f -name '*.txt' -exec grep pippo {} +

 writes:
AM>gb>
AM>gb>AM> paolob wrote:
AM>gb>AM>pb> Ciao
AM>gb>AM>pb> mi serve cercare tutti i file che contengono ad esempio le parole
AM>gb>AM>pb> pippo e pluto
AM>gb>AM>pb> non necessarimente sulla stessa linea....
AM>gb>AM>pb> qualche idea ?
AM>gb>AM>pb> grazie
AM>gb>AM>
AM>gb>AM> puoi usare l'opzioen -regex di find
AM>gb>AM>
AM>gb>AM> se cerchi su tutto l'hard disk devi usare sudo
AM>gb>AM>
AM>gb>AM> $ sudo find / -regex ".*pippo.*" -regex ".*pluto.*"
AM>gb>
AM>gb> il manuale di find, a proposito di -regex, dice
AM>gb>   -regex pattern
AM>gb>      File name matches regular expression pattern.
AM>gb> e non "File content matches regular expression pattern."
AM>>
AM> vero.
riquoto tutto il thread, rileggiti se vuoi. adios

 qualche idea ?
    Basta grep, senza altri comandi:
grep -lERi '(pippo)|(pluto)' directory/
   Ciao,
-- 
Alessandro Selli 
http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.
Chiave PGP/GPG: EC885A8B

 grep -lERi '(pippo)|(pluto)' directory/
Ma nemmeno per sogno.


   Invece di sognare, le cose falle:
[alessandro@granchio ~]$ mkdir -p /tmp/un/livello/e/piu/di/directory
[alessandro@granchio ~]$ echo 'File contenente Pippo' > /tmp/un/livello/file1
[alessandro@granchio ~]$ echo 'Un altro file contenente pluto' > 
/tmp/un/livello/e/piu/di/directory/altro_file
[alessandro@granchio ~]$ grep -lERi '(pippo)|(pluto)' /tmp
grep: /tmp/orbit-gdm: Permission denied
grep: /tmp/pulse-IN7l1pdRh2DN: Permission denied
grep: /tmp/.X11-unix/X0: No such device or address
/tmp/un/livello/file1
/tmp/un/livello/e/piu/di/directory/altro_file
[alessandro@granchio ~]$
QED


 i file con caratteri strani nei nomi (\n su tutti, anche se e' raro). Per 
ah... bellina... solo per il gusto di provare, ci ho provato
creo 6 file tutti con un carattere di \n nel nome
poi li ciclo dentro ad un while, e su ognuno ci scrivo dentro "ok"
$ touch {a,b,c}$'\n'{1,2,3}
$ ls
a?1  a?2  a?3  b?1  b?2  b?3  c?1  c?2  c?3
$ hd <(ls a*1)
00000000  61 0a 31 0a                                       |a.1.|
00000004
$ find . -type f -print0 | while read -d "`echo -en \"\x00\"`"; do echo 
ok > "$REPLY"; done
$ cat *
ok
ok
ok
ok
ok
ok
ok
ok
ok


eppure alla risposta prima mi sembravi intelligente...


la realta' a volte supera la fantasia!

Alessandro Selli wrote:

Magari leggere il testo del problema...
"mi serve cercare tutti i file che contengono ad esempio le parole pippo e 
pluto"
keyword: "e"

> la realta' a volte supera la fantasia!
ops... no... la fantasia sta ancora avanti
manca l' &  nelle espressioni regolari.
c'e' solo l' |

 ok > "$REPLY"; done
se qualche pazzo e' finito qui, correggo... si puo' scrivere un po' piu' 
semplicemente cosi'
$ find . -type f -print0 | while read -d $'\x00'; do echo ok > "$REPLY"; 
done

Antonio Macchi wrote:

while IFS= read -r -d '' etc.

> while IFS= read -r -d '' etc.
l'opzione -d '' funziona benone!
e l'altra, la -r, credo sia proprio obbligatoria per poter usare tutti i 
tipi di nomi di file
ho fatto una prova...
$ touch a\\b
$ ls
a\b
$ find . -type f -print0 | while read -d ''; do echo ok > "$REPLY"; done
$ ls
a\b  ab
$ cat ab
ok
$ cat a?b
$
il file "a\b" l'ha letto come "ab"
$ rm ab
$ find . -type f -print0 | while read -r -d ''; do echo ok > "$REPLY"; done
$ ls
a\b
$ cat *
ok
(SPQL)

 /tmp/un/livello/file2
[alessandro@granchio ~]$ echo 'Quest'\''altro file contiene pure lui
tanto pluto che pippo' > /tmp/un/livello/e/piu/di/directory/un_altro_file
[alessandro@granchio ~]$ echo -e 'E infine questo file contiene pluto su
una riga\ne poi pippo su u'\''altra' > /tmp/un/livello/ancora_un_file
[alessandro@granchio ~]$ grep -lERi 'pluto' /tmp | xargs grep -li 'pippo'
grep: /tmp/gpg-SuyySX/S.gpg-agent: No such device or address
grep: /tmp/gpg-bHK44j/S.gpg-agent.ssh: No such device or address
grep: /tmp/.X11-unix/X0: No such device or address
grep: /tmp/orbit-tecnico/linc-cc7-0-5fc2779dbb7e: No such device or address
grep: /tmp/orbit-tecnico/linc-cb0-0-45eced92232ae: No such device or address
/tmp/un/livello/file2
/tmp/un/livello/e/piu/di/directory/un_altro_file
/tmp/un/livello/ancora_un_file
[alessandro@granchio ~]$
  Ora direi che ci siamo.  O c'è un'altra fantasia che batte la realtà?  :-)
[alessandro@granchio ~]$ [[ reale > fantasia ]] && echo "Eccome che no"
|| echo "Tu voi fa' l'americano"
Eccome che no
[alessandro@granchio ~]$
  E fu pace ed equilibrio nello userland.


   :-)
Quasi. Quel codice ha problemi con nomi di file contenenti spazi e altri 
caratteri strani.
Aggiungi -Z alle opzioni del primo grep e -0 a quelle di xargs, e arrivi a 
una cosa simile ad un'altra soluzione gia' postata in precedenza.

   E fu pace ed equilibrio nello userland.
equicosa?

 una cosa simile ad un'altra soluzione gia' postata in precedenza.
perfezione raggiunta allora!
ale'.

Mi spiace, ho letto solo ora tutte le risposte,
non velevo alzare tanta polvere,
ma cio' dimostra che non c'era un modo semplice e pulito
per fare questa ricerca.
Di fatto mi sono scritto un programma in c perche' la velocita'
era uno dei requisiti che mi sono nati nel frattempo, oltre ad avere
un approccio trasparente con le "bastardedentro" lettere accentate
ad esempio in pratica trovare un documento tipo :
Noi manipolo di fratelli: poiché chi oggi verserà il suo sangue con me
sarà mio fratello,
e per quanto umile la sua condizione, sarà da questo giorno elevata, e
tanti gentiluomini
ora a letto in patria si sentiranno maledetti per non essersi trovati
oggi qui, e menomati nella
loro virilità sentendo parlare chi ha combattuto con noi questo giorno
di San Crispino
mi serviva cercare le parole "poiche crispino" senza pretentedere che
l'utente si ricordasse l'accento in poiché ;-)
cmq grazie a tutti
Ciao
Chiaramente mi stanno chiedendo di trovare le parole simili
GRRRRRRRRRRRRRR

 per fare questa ricerca.
Perl? Ti dice niente?

 l'utente si ricordasse l'accento in poiché ;-)
Ammesso che tu non sappia niente delle sequenze di collazione basta:
grep 'poich[eèé]' ...

 grep 'poich[eèé]' ...
ehehe hai mai parlato con un utente di sequenze di collazione ?

 confronti queste con le prestazioni delle altre soluzioni.
PS per i confronti non mi metto certo a rifare il programma
ma a titolo di prova una ricerca tipo su 11657 file e trovati 957
con grep -i -l
real    0m1.855s
user    0m1.730s
sys     0m0.130s
con il mio grep
real    0m0.344s
user    0m0.070s
sys     0m0.280s