L'ho usato molto, il miglioramento che cerco di ottenere è quello di evitare i nomi dei file di eco che non corrispondono in grep. Modo migliore per farlo?
for file in `find . -name "*.py"`; do echo $file; grep something $file; done
L'ho usato molto, il miglioramento che cerco di ottenere è quello di evitare i nomi dei file di eco che non corrispondono in grep. Modo migliore per farlo?
for file in `find . -name "*.py"`; do echo $file; grep something $file; done
Risposte:
find . -name '*.py' -exec grep something {} \; -print
stampa il nome del file dopo le righe corrispondenti.
find . -name '*.py' -exec grep something /dev/null {} +
stamperebbe il nome del file davanti a ogni riga corrispondente (aggiungiamo /dev/null
per il caso in cui c'è solo un file corrispondente poiché grep
non stampa il nome del file se viene passato solo un file in cui cercare. L'implementazione GNU di grep
ha -H
un'opzione per quello come alternativa).
find . -name '*.py' -exec grep -l something {} +
stamperebbe solo i nomi dei file che hanno almeno una riga corrispondente.
Per stampare il nome del file prima delle righe corrispondenti, è possibile utilizzare invece awk:
find . -name '*.py' -exec awk '
FNR == 1 {filename_printed = 0}
/something/ {
if (!filename_printed) {
print FILENAME
filename_printed = 1
}
print
}' {} +
Oppure chiama grep
due volte per ogni file, anche se sarebbe meno efficiente in quanto eseguirà almeno un grep
comando e fino a due per ogni file (e leggerà il contenuto del file due volte):
find . -name '*.py' -exec grep -l something {} \; \
-exec grep something {} \;
In ogni caso, non vuoi passare in rassegna l'output di find
così e ricordati di citare le tue variabili .
Se volevi usare un loop shell, con gli strumenti GNU:
find . -name '*.py' -exec grep -l --null something {} + |
xargs -r0 sh -c '
for file do
printf "%s\n" "$file"
grep something < "$file"
done' sh
(funziona anche su FreeBSD e derivati).
Se stai usando GNU grep, puoi usare la sua -r
o --recursive
opzione per fare questa semplice ricerca per te:
grep -r --include '*.py' -le "$regexp" ./ # for filenames only
grep -r --include '*.py' -He "$regexp" ./ # for filenames on each match
Hai bisogno solo find
se hai bisogno di predicati più avanzati.
grep
, grep
può o meno guardare all'interno dei symlink o attraversare i symlink verso le directory. Potresti anche trovare alcune variazioni nella gestione di altri tipi di file non regolari.
Puoi dire a grep di includere il nome del file nell'output. Quindi se c'è una corrispondenza verrà mostrata sulla console; se non esiste alcuna corrispondenza all'interno di un file, nessuna riga verrà stampata per quel file.
find . -name "*.py" | xargs grep -n -H something
Dal man grep
:
-H Always print filename headers with output lines
-n, --line-number
Each output line is preceded by its relative line number in the file, starting at line 1. The line number counter is reset for each file processed.
This option is ignored if -c, -L, -l, or -q is specified.
Se i tuoi file potrebbero avere nomi con spazi, devi cambiare la pipe per usare i caratteri NUL come separatore. Il comando completo ora avrà questo aspetto:
find . -name "*.py" -print0 | xargs -0 grep -n -H something
Puoi provare qualcosa del tipo:
find . -name "*.py:" -exec grep -l {} \;
Questo comando exec grep per ogni file, scoperto dal comando find e dalla sua funzione standard di comando find
Esistono grep
alternative che, per impostazione predefinita, producono i risultati nel formato desiderato. I 2 più popolari che conosco sono ag
(aka "il cercatore d'argento") e ack
. ag
è pubblicizzato come alternativa più veloce a ack
.
$ ag '^\w+\s*\w+\(' ~/build/i3/src
build/i3/src/display_version.c
58:void display_running_version(void) {
build/i3/src/load_layout.c
42:static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
518:json_content_t json_determine_content(const char *filename) {
575:void tree_append_json(Con *con, const char *filename, char **errormsg) {
build/i3/src/x.c
64:CIRCLEQ_HEAD(state_head, con_state) state_head =
67:CIRCLEQ_HEAD(old_state_head, con_state) old_state_head =
70:TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head =
97:void x_con_init(Con *con, uint16_t depth) {
...
Non posso mostrarti qui, ma l'output è ben colorato. Ottengo i nomi dei file in un verde oliva, i numeri delle righe in giallo oro e il pezzo abbinato in ogni riga in rosso sangue. I colori sono comunque personalizzabili.