In che modo una shell (bash, ad esempio) espande i pattern jolly?


9

Supponiamo che una directory abbia 100 file che iniziano con la lettera "a".

Se eseguo una operazione grep <some string> a*dal terminale, come gestirà la shell?

Espanderà l'espressione regolare, otterrà un elenco di tutti i file che iniziano con a e grep su ognuno di quelli in sequenza? O c'è un altro modo?

Supponiamo che io abbia una matrice dei nomi di file sopra che iniziano con 'a'. Ci vorrà più / meno tempo se scrivo un ciclo for e eseguo io stesso l'iterazione in uno script shell o in un programma ac?


7
A proposito, non è globun'espressione regolare. Grande differenza.
Aaron D. Marasco il

Risposte:


8

Innanzitutto, un nitpick: una stringa come a*nella normale sintassi della shell è un glob, che funziona in modo diverso dalle espressioni regolari.

In una panoramica di alto livello, l'interprete della shell (ovvero bash) espande la stringa a*in un elenco di tutti i nomi di file corrispondenti al modello a*. Questi diventano quindi parte dei parametri della riga di comando in una singola istanza di grep(per i programmatori, tutte le parole espanse vanno come stringhe separate argvnell'argomento di main). Quel singolo grepcomando quindi analizza gli argomenti in qualunque modo scelga, e spetta grepa interpretarli come nomi di file, opzioni, argomenti di opzioni, espressioni regolari, ecc. E intraprendere le azioni appropriate. Tutto avviene in sequenza (AFAIK nessuna grepimplementazione utilizza più thread).

Se si implementa un ciclo in uno script di shell per fare la stessa cosa, è quasi garantito che sia più lento del processo precedente, per i seguenti motivi. Se si genera un nuovo processo grep per ogni file, sarà sicuramente più lento a causa del sovraccarico della creazione del processo che si moltiplica inutilmente. Se hai creato tu stesso la lista degli argomenti nello script della shell e grephai usato una singola istanza di , tutto ciò che fai nella shell sarà ancora più lento perché i comandi della shell devono essere interpretati (da bash), il che aggiunge un ulteriore livello di codice, e tu basta implementare nuovamente ciò che bash stava già facendo più velocemente internamente nel codice compilato.

Per quanto riguarda la scrittura da soli in C, è possibile ottenere facilmente prestazioni comparabili con il processo descritto nel primo paragrafo, ma è improbabile che si riesca a ottenere un guadagno di prestazioni sufficiente rispetto alle attuali implementazioni grep / bash per giustificare il tempo spesi senza approfondire le ottimizzazioni delle prestazioni specifiche della macchina o sacrificare la portabilità. Forse potresti provare a trovare una versione arbitrariamente parallelizzabile di grep, ma anche questo potrebbe non essere utile in quanto è più probabile che tu sia associato a I / O rispetto a CPU. Espansione globale e grep sono già "abbastanza veloci" per la maggior parte degli scopi "normali".


Grazie per la risposta molto dettagliata. In realtà, ho bisogno di grep file gzip (pochi GB ciascuno). Ho un elenco di quei file. Ora ho la possibilità di scegliere se creare una regex (complicata) per abbinare quei file o scorrere l'elenco noto ed eseguire grep su ognuno di questi (facile). Da qui la preoccupazione per le prestazioni.
harithski,

prova zcate zgrep; non c'è bisogno di decomprimerli uno per uno
jw013

Sì, naturalmente. Sto usando zgrep.
harithski

6

Sì, si espanderà in un elenco di file e invierà l'elenco risultante al grepprogramma. Almeno questo è ciò che man bashdice nella sottosezione Espansione percorso .

C'è un altro modo di usare l'espansione in casi semplici, come dici tu: scrivi grep <some_string> ae prima di premere* , premi ESC. Questo espande l'elenco dei file corrispondenti direttamente nella riga di comando, in modo da poter verificare che l'elenco sia OK prima di premere Enter.

Per quanto riguarda la seconda parte della tua domanda, dipende. Se intendi scrivere un ciclo for che esegue grep su ciascuno dei file a turno, allora sarebbe sicuramente più lento, perché il programma grep verrà eseguito non una volta, ma una volta per file. Tuttavia, ciò che è importante tenere a mente è che esiste un certo limite sulla lunghezza estesa degli argomenti della riga di comando che è possibile utilizzare, sebbene in genere sia piuttosto elevato. Per vederlo, puoi provare grep adasdsadf /usr/*/*/* >/dev/null.


2
ESC+*non è esattamente lo stesso che lasciare bash espandersi * perché ESC+*inserirà dotfile (nomi che iniziano con a .) mentre l'espansione di *dipende dotglob shoptdall'impostazione. La sequenza di tasti per espandere e inserire globs è C-x *di default e si associa al comando readline glob-expand-word.
jw013,

1
@ jw013 Grazie per l'informazione! Non sembra cambiare il caso a*dell'espansione, ma è certamente importante in un ambito più ampio.
rozcietrzewiacz,

2
zshnota: premendo semplicemente il tasto tab sui parametri espandibili (pattern glob, espansione parentesi, sostituzione comando, ...) li espanderà.
Stéphane Gimenez,

@ jw013 In realtà, ho appena testato il C-xcollegamento e non espande l'elenco dei file sul mio sistema (usando bash).
rozcietrzewiacz,

1
@roz Giusto - Non lo uso quasi mai, volevo solo sottolineare la differenza (piuttosto nitida) :). C-x *fa solo globs che fanno solo nomi di file, ma in Esc *realtà fa molto di più da allora insert-completions, come in tutti i possibili completamenti. Ciò significa che l'utilizzo Esc *su una riga di comando vuota inserirà il nome di ogni singolo file eseguibile nel tuo $PATH, ad esempio.
jw013,
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.