Come usare grep su tutti i file in modo non ricorsivo in una directory?


35

Voglio cercare una stringa di testo in tutti i file in una directory (e non nelle sue sottodirectory; so che l' -ropzione lo fa, ma non è quello che voglio).

  1. In esecuzione

    grep "string" /path/to/dir
    

    dovrebbe essere in grado di farlo, ho letto, ma mi dà l'errore:

    grep: dir: è una directory

  2. Successivamente, ho provato a correre grepsu più file.

    grep "string" .bashrc .bash_aliases funziona perfettamente.

    grep "string" .bash* funziona anche come previsto.

    grep "string" * mi dà gli errori:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

Vengono stampati solo gli errori, non visualizzo le righe corrispondenti. Ho provato a usare l' -sopzione, ma senza risultati.

Quindi, le mie domande:

  1. Perché non riesco a utilizzare grepuna directory, come in (1), quando dovrei essere in grado di farlo? L'ho visto fatto in molti esempi su Internet.
    Modifica : quando dico "usando grep su una directory", intendo "cerca in tutti i file in quella directory escludendo le sue sottodirectory". Credo che questo sia ciò che Grep fa quando gli passi una directory al posto di un file. Sono errato?

  2. Per favore, mi dia una spiegazione sul funzionamento grepche spiegherebbe il comportamento dei comandi in (2).
    Modifica : lasciami essere più specifico. Perché usare i caratteri jolly per specificare più file in cui cercare il lavoro con .bash*e non con *o addirittura ./*?

  3. Come posso cercare tutti i file in una directory (e non nelle sue sottodirectory) usando grep?


Inoltre stai facendo affidamento sulla shell espandendo i caratteri jolly come *, noto come globbing. Globbing non include i nomi di file che iniziano con un punto .bashrccome standard. Puoi impostare le opzioni della shell in modo che includano questi file, ma puoi metterti in un pasticcio se non sai cosa stai facendo. Una buona guida per comprendere il globing può essere trovata qui mywiki.wooledge.org/glob
Arronical

Non so perché, ma ho sempre cercato di muovere i file nascosti e ha sempre funzionato. Non ho cambiato nessuna impostazione o qualcosa del genere. Come ho sottolineato in (2), funziona grep "string" .bash*anche con .
John Red,

Spiacenti, il mio ultimo esempio non è corretto. Puoi anche cercare file nascosti e sopprimere "è una directory" perché Linux vede tecnicamente le directory come un diverso tipo di file. Il comando sarebbe quindi: grep "string" * .* 2>/dev/nulloppuregrep -s "string" * .*
Terrance

Risposte:


41

In Bash, un glob non si espanderà in file nascosti, quindi se si desidera cercare tutti i file in una directory, è necessario specificare i file nascosti .*e non nascosti *.

Per evitare gli errori "È una directory", è possibile utilizzare -d skip, ma sul mio sistema ricevo anche un errore grep: .gvfs: Permission denied , quindi suggerisco di utilizzare -s, che nasconde tutti i messaggi di errore.

Quindi il comando che stai cercando è:

grep -s "string" * .*

Se stai cercando file in un'altra directory:

grep -s "string" /path/to/dir/{*,.*}

Un'altra opzione è quella di utilizzare l' dotglobopzione shell, che farà in modo che un glob includa file nascosti.

shopt -s dotglob
grep -s "string" *

Per i file in un'altra directory:

grep -s "string" /path/to/dir/*

† Qualcuno ha detto che non avrei dovuto ricevere questo errore. Potrebbero avere ragione - ho fatto un po 'di lettura ma non sono riuscito a capirlo o a fare la coda da solo.


C'è qualche motivo per lo spazio tra *e .*?
Hashim,

2
@Hashim Confronta l'output di echo * .*ed eseguilo echo *.*nella tua home directory, e la differenza dovrebbe essere ovvia. Altrimenti LMK e te lo spiego.
wjandrea,

Interessante, quindi echo *mostra file e cartelle echo *.*non nascosti, echo .*mostra file non nascosti, mostra tutti i file e echo * .*mostra tutti i file e le directory. Ma perché la ragione dello spazio tra i due in quest'ultimo caso? Mi sembra disordinato. Non esiste un modo per combinare i due per ottenere gli stessi risultati? O altrimenti c'è una spiegazione della sintassi del perché i due debbano essere separati qui, o è * .*un caso eccezionale?
Hashim,

1
@Hashim Non sono sicuro di come sei arrivato a queste conclusioni, quindi lasciami spiegare. Innanzitutto, le directory sono file in questo contesto. In globs, *rappresenta tutti i file non nascosti (ovvero nomi di file che non iniziano con un punto); .*rappresenta tutti i file nascosti (ad esempio nomi di file che non iniziano con un punto); e *.*rappresenta tutti i file non nascosti che contengono un punto. In echo * .*, i due globs devono essere separati perché sono globs diversi: uno per non nascosto, uno per nascosto. Anche se come ho scritto nella mia risposta, puoi fare in modo che *includa i file nascosti attivando l' dotglobopzione shell.
wjandrea,

1
L'uso *.*è comune su Windows (DOS) come un modo per elencare tutti i file ma su * nix includerà solo i file con un punto, quindi non ha senso su * nix. Invece si usa *per elencare tutti i file tranne i file nascosti e .*per elencare i file nascosti.
thomasrutter,

11

È necessaria l' -d skipopzione aggiunta su.

  1. Grep sta cercando all'interno dei file. Puoi cercare ricorsivamente, come hai detto, se vuoi cercare file all'interno di una directory.

  2. Per impostazione predefinita, grep legge tutti i file e rileva le directory. Perché di default non hai definito cosa fare con le directory con l' -dopzione, dà un output di errore.

  3. La ricerca nella directory principale sarebbe grep -d skip "string" ./*


Per ulteriori informazioni su grep, vedere man grep.
anonymous2

(a) Si prega di consultare la modifica. (b) L'uso -d skipnon funziona; è praticamente lo stesso di -s; vedi anche la modifica. (c) No, grep -d skip "string" ./*non funziona neanche.
John Red,

7

I vecchi timer probabilmente lo farebbero:

find . -type f -print0 | xargs -0 grep "string"

3
Perché no find . -type f -exec grep string {} +?
wchargin,

5
Anche tu vuoi -maxdepth 1.
wchargin,

@wchargin: se vuoi il nome del file nell'output quando c'è un solo file penso che tu vogliafind . -type f -maxdepth 1 -exec grep string /dev/null {} +
Gregory Nisbet

1
@GregoryNisbet: passa -Ha grep.
mercoledì

2

Riprascrizione: vuoi eseguire il grep dei file in un livello di sottodirectory, ma non ricorrere a tutte le sottodirectory?

grep forthis  *  */*

O se non vuoi i file nella directory corrente

grep forthis  */*

Nota che questo non troverà directory che iniziano con un punto.

grep forthis  .*/*    */*   

dovrebbe fare quel lavoro.

C'è anche -maxdepthe -mindepthparametri disponibili restrizione al findcomando di troppo.


Non grep forthis */*cercare i file sia nella directory corrente che in una directory in basso?
Hashim,

Per lo più @Hashim no, perché */*abbina le cose solo con una barra. Se avessi un file chiamato a/bnella directory corrente, `* / * corrisponderebbe a quello.
Criggie,

0

Ecco un esempio per saltare le directory senza saltare tutti gli errori:

grep --directories='skip' 'searchString' *
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.