Come cercare file in cui esistono due parole diverse?


14

Sto cercando un modo per cercare file in cui esistono due istanze di parole nello stesso file. Ho utilizzato quanto segue per eseguire le mie ricerche fino a questo punto:

find . -exec grep -l "FIND ME" {} \;

Il problema in cui mi imbatto è che se non esiste esattamente uno spazio tra "FIND" e "ME", il risultato della ricerca non produce il file. Come posso adattare la precedente stringa di ricerca in cui entrambe le parole "TROVA" e "ME esistono in un file invece di" TROVA ME "?

Sto usando AIX.


1
Le parole esistono ovunque nel file o sono sempre sulla stessa riga?
Sobrique,

L'intento era la stessa linea.
Chad Harrison,

Un'alternativa, se le parole sono sulla stessa riga, è usare un'espressione regolare con grep -E/ egrepche descriva tutti gli schemi a cui sei interessato (e usare +invece che ;se la tua ricerca ha supporto per +.
MattBianco

Risposte:


21

Con gli strumenti GNU:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

Puoi fare in modo standard:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

Ma ciò comporterebbe due greps per file. Per evitare di eseguire così tanti se grepessere comunque portatile pur consentendo qualsiasi carattere nei nomi dei file, è possibile fare:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

L'idea è quella di convertire l'output di findin un formato adatto a xargs (che prevede uno spazio vuoto (SPC / TAB / NL e gli altri spazi vuoti dalle impostazioni locali con alcune implementazioni di xargs) elenco separato di parole in cui è possibile inserire virgolette singole, doppie e barre rovesciate sfuggire agli spazi vuoti e l'un l'altro).

Generalmente non è possibile post-elaborare l'output di find -print, perché separa i nomi dei file con un carattere di nuova riga e non sfugge ai caratteri di nuova riga che si trovano nei nomi di file. Ad esempio se vediamo:

./a
./b

Non abbiamo modo di sapere se si tratta di un file chiamato bin una directory chiamata a<NL>.o se sono i due file ae b.

Usando .//., perché //non può apparire diversamente in un percorso di file come output di find(perché non esiste una directory con un nome vuoto e /non è consentita in un nome di file), sappiamo che se vediamo una riga che contiene //, allora quello è la prima riga di un nuovo nome file. Quindi possiamo usare quel awkcomando per sfuggire a tutti i caratteri di nuova riga tranne quelli che precedono quelle righe.

Se prendiamo l'esempio sopra, findoutput nel primo caso (un file):

.//a
./b

Quale awk sfugge a:

.//a\
./b

Quindi questo lo xargsvede come un argomento. E nel secondo caso (due file):

.//a
.//b

Che awklascerebbe così com'è, quindi xargsvede due argomenti.


Perché non usare find ... -print0e grep --nullinvece?
razziato il

@razzed, non sono sicuro di cosa tu voglia dire quelli. grep --null(aka -Z) è usato nel primo ma è un'estensione GNU. -print0(un'altra estensione GNU) non sarebbe d'aiuto qui.
Stéphane Chazelas,

Grazie. Vorrei racchiudere il codice della shell in uno script che prende la directory di ricerca come argomento dalla riga di comando. Non sono molto sicuro di cosa .//.significhi ancora, e mi chiedo come posso modificarlo per accettare un argomento dalla riga di comando, diciamo $1?
Tim

Grazie. Ai tuoi comandi, è necessario usare -print0con finde -0con xargs?
Tim

@ Tim, non sono sicuro di cosa intendi. Non uso da find -print0nessuna parte nella mia risposta.
Stéphane Chazelas,

8

Se i file sono in una singola directory e il loro nome non contengono spazio, tabulazione, nuova riga, *, ?[i personaggi e non iniziano con -., questo otterrà un elenco di file contenenti ME, quindi restringere che verso il basso per quelli che contiene anche TROVA.

grep -l FIND `grep -l ME *`

Questo ha bisogno di più voti !! Molto più elegante della risposta "accettata". Ha funzionato per me.
roblogic

Ho appena fatto la grep -l CategoryLinearAxis `grep -l labelJsFunction *`ricerca di file con entrambi gli attributi. Che modo perfetto per farlo. +1
WEBjuju

3

Con awkte puoi anche eseguire:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

Usa cxe cyper contare le linee corrispondenti FINDe rispettivamente ME. Nel ENDblocco, se entrambi i contatori> 0, stampa il FILENAME.
Questo sarebbe più veloce / più efficiente con gnu awk:

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +

2

Oppure usa egrep -eo in grep -Equesto modo:

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

o

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

Le +marche trovano (se supportato) aggiungere più file (percorso) nomi come argomenti al comando di essere -execed. Ciò consente di salvare i processi ed è molto più veloce di quello \;che richiama il comando una volta per ogni file trovato.

-type f corrisponde solo ai file, per evitare il grepping su una directory.

'(ME.*FIND|FIND.*ME)'è un'espressione regolare corrispondente a qualsiasi riga contenente "ME" seguito da "FIND" o "FIND" seguito da "ME". (virgolette singole per impedire alla shell di interpretare caratteri speciali).

Aggiungi -ia al grepcomando per renderlo senza distinzione tra maiuscole e minuscole.

Per abbinare solo le linee in cui "TROVA" precede "ME", utilizzare 'FIND.*ME'.

Per richiedere spazi (1 o più, ma nient'altro) tra le parole: 'FIND +ME'

Per consentire spazi (0 o più, ma nient'altro) tra le parole: 'FIND *ME'

Le combinazioni sono infinite con le espressioni regolari e, purché tu sia interessato ad abbinarle solo su una riga alla volta, egrep è molto potente.


La maggior parte dei greps non supporta "-r"? Ciò eliminerebbe la "ricerca", ma potrebbero esserci socket o altri file non semplici nell'albero da cercare.
rubato l'

OP utilizza AIX e ha avuto findnella domanda.
MattBianco,

0

Guardando la risposta accettata, sembra più complesso di quanto debba essere. Versioni GNU di finde grepe xargsdi supporto stringhe terminati da NULL. È semplice come:

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

È possibile modificare il findcomando per filtrare i file desiderati e funziona con nomi di file contenenti qualsiasi carattere; senza l'ulteriore complessità seddell'analisi. Se si desidera elaborare ulteriormente i file, aggiungerne un altro --nullall'ultimogrep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

E, in funzione:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

Ovviamente, usa la risposta accettata se non stai eseguendo versioni GNU di questi strumenti.


1
--null, --print0, -0Sono tutte le estensioni GNU. Anche se alcuni di questi si trovano in altre implementazioni al giorno d'oggi, non sono ancora portatili e non nello standard POSIX o Unix.
Stéphane Chazelas,
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.