bash che combina l'espansione jolly con l'espansione del controvento


8

Sto cercando di espandere una stringa che coinvolge un carattere jolly e una raccolta di estensioni specificata tra parentesi graffe. Nulla sembra funzionare come illustrato nell'esempio seguente. la variabile firstListespande bene, ma né secondList, thirdListo fourthListespande correttamente. Ho anche provato varie versioni di evalma nessuna funziona neanche. Qualsiasi aiuto sarebbe apprezzato

#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls  $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList 
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList  
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"

fwiw eval ls $secondListfunziona bene qui ... cosa stai cercando di realizzare?
don_crissti,

È necessario controllare l' ordine delle espansioni bash - l'espansione del controvento si verifica prima delle espansioni dei paramater. Per ottenere l'effetto che speri, devi evalottenere un secondo round di espansioni.
Glenn Jackman,


@glennjackman C'è una soluzione senza valutazione. Guarda la seconda soluzione alla fine della mia risposta .

Risposte:


7

La shell si espande *solo se non quotata, qualsiasi quotazione interrompe l'espansione della shell.

Inoltre, un'espansione di parentesi graffe deve essere annullata per essere espansa dalla shell.

Questo lavoro (usa echo per vedere cosa fa la shell):

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Anche se ci sono file con altri nomi:

$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1  a.ext2  b.ext1  b.ext2  c.ext3  c.ext4  d.ext3  d.ext4  none

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Perché funziona

È importante capire perché funziona. È a causa dell'ordine di espansione. Prima "Espansione parentesi graffa" e poi (l'ultima) "Espansione percorso" (aka espansione globale).

Brace --> Parameter (variable) --> Pathname

Possiamo disattivare "Espansione percorso" per un momento:

$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2

"Pathname Expansion" riceve due argomenti: *.ext1e *.ext2.

$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Il problema è che non possiamo usare una variabile per l'espansione del controvento.
È stato spiegato molte volte in precedenza per l' utilizzo di una variabile all'interno di una "Espansione del rinforzo"

Per espandere un "Espansione del rinforzo" che è il risultato di un "Espansione variabile", è necessario inviare nuovamente la riga di comando alla shell con eval.

$ list={ext1,ext2}
$ eval echo '*.'"$list"

Parentesi graffa -> Variabile -> Glob || -> Parentesi graffa -> Variabile -> Glob
........ citato qui -> ^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^

I valori dei nomi dei file non comportano problemi di esecuzione per eval:

$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2

Ma il valore di $listpotrebbe non essere sicuro. Tuttavia, il valore di $listè impostato dallo script writer. Lo script writer ha il controllo di eval: Basta non usare valori impostati esternamente per $list. Prova questo:

#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"

Un'alternativa migliore.

Un'alternativa (senza valutazione) è usare Bash "Extended Patterns" :

#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list

Nota: tenere presente che entrambe le soluzioni (eval e pattern) (come scritto) sono sicure per i nomi di file con spazi o nuove righe. Ma fallirà per a $listcon spazi, perché $listnon è quotato o eval rimuove le virgolette.


Sì, prolungato affanno
glenn jackman,

2

Prendere in considerazione:

secondList='*.{ext1,ext2}'
ls $secondList 

Il problema è che l' espansione del controvento viene eseguita prima dell'espansione variabile . Ciò significa che, in quanto sopra, l'espansione del controvento non viene mai eseguita.

Questo perché, quando bash vede per la prima volta la riga di comando, non ci sono parentesi graffe. Dopo secondListè stato espanso, è troppo tardi.

Funzionerà quanto segue:

$ s='*'
$ ls $s.{ext1,ext2}
a.ext1  a.ext2  b.ext1  b.ext2

Qui, la riga di comando ha parentesi graffe in modo che l' espansione delle parentesi graffe possa essere eseguita come primo passo. Successivamente, il valore di $sviene sostituito in ( espansione variabile ) e infine viene eseguita l' espansione del nome percorso .

Documentazione

man bash spiega l'ordine di espansione:

L'ordine delle espansioni è: espansione delle parentesi graffe; espansione della tilde, espansione di parametri e variabili, espansione aritmetica e sostituzione dei comandi (eseguita da sinistra a destra); divisione delle parole; e l'espansione del nome percorso.

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.