Come posso evitare il carattere jolly / asterisco in bash?


136

Per esempio:

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

e usando il \carattere di escape:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

Sto ovviamente facendo qualcosa di stupido.

Come ottengo l'output BAR * BAR?

Risposte:


139

Citando quando l'impostazione $FOOnon è sufficiente. È necessario citare anche il riferimento alla variabile:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR

8
questo è misterioso, perché questo? cosa sta succedendo?
Tofutim,

Perché le variabili si espandono
Daniel,

103

RISPOSTA BREVE

Come altri hanno già detto, dovresti sempre citare le variabili per prevenire comportamenti strani. Quindi usa echo "$ foo" invece di echo $ foo .

RISPOSTA LUNGA

Penso che questo esempio meriti ulteriori spiegazioni perché c'è molto di più di quanto potrebbe sembrare in faccia.

Posso vedere dove arriva la tua confusione perché dopo aver eseguito il tuo primo esempio probabilmente hai pensato a te stesso che la shell ovviamente sta facendo:

  1. Espansione dei parametri
  2. Espansione del nome file

Quindi dal tuo primo esempio:

me$ FOO="BAR * BAR"
me$ echo $FOO

Dopo che l'espansione dei parametri è equivalente a:

me$ echo BAR * BAR

E dopo l'espansione del nome del file è equivalente a:

me$ echo BAR file1 file2 file3 file4 BAR

E se digiti semplicemente echo BAR * BARnella riga di comando vedrai che sono equivalenti.

Quindi probabilmente hai pensato a te stesso "se sfuggo al *, posso impedire l'espansione del nome file"

Quindi dal tuo secondo esempio:

me$ FOO="BAR \* BAR"
me$ echo $FOO

Dopo l'espansione dei parametri dovrebbe essere equivalente a:

me$ echo BAR \* BAR

E dopo l'espansione del nome file dovrebbe essere equivalente a:

me$ echo BAR \* BAR

E se provi a digitare "echo BAR \ * BAR" direttamente nella riga di comando, verrà effettivamente stampato "BAR * BAR" perché l'espansione impedisce l'espansione del nome file.

Quindi perché usare $ foo non ha funzionato?

È perché c'è una terza espansione che ha luogo: la rimozione delle quotazioni. Dalla rimozione manuale del preventivo bash è:

Dopo le espansioni precedenti, tutte le occorrenze non quotate dei caratteri '\', '' 'e' "'che non sono risultate da una delle espansioni di cui sopra vengono rimosse.

Quindi ciò che accade è quando si digita il comando direttamente nella riga di comando, il carattere di escape non è il risultato di una precedente espansione, quindi BASH lo rimuove prima di inviarlo al comando echo, ma nel 2 ° esempio, "\ *" era il risultato di una precedente espansione dei parametri, quindi NON viene rimosso. Di conseguenza, l'eco riceve "\ *" ed è quello che stampa.

Nota la differenza tra il primo esempio: "*" non è incluso nei caratteri che verranno rimossi dalla Rimozione preventivo.

Spero che abbia senso. Alla fine la conclusione è la stessa: basta usare le virgolette. Ho solo pensato di spiegare perché l'escaping, che logicamente dovrebbe funzionare se sono in gioco solo le espansioni di Parameter e Filename, non ha funzionato.

Per una spiegazione completa delle espansioni BASH, consultare:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions


1
Bella risposta! Ora non mi sento di fare una domanda così stupida. :-)
andyuk,

1
Esiste qualche utilità per sfuggire ai personaggi speciali?
Jigar Joshi,

"dovresti sempre citare le variabili per prevenire comportamenti strani" - quando vuoi usarle come stringhe
Angelo


Mentre la risposta di @finnw in alto risponde direttamente alla domanda, questa risposta è molto meglio nello spiegare il perché di ciò, il che aiuta molto di più a estrapolare come funzionerebbe nei nostri scenari. Questo è il tipo di risposta di cui abbiamo bisogno per vedere di più.
Nicolas Coombs,

54

Aggiungerò un po 'a questo vecchio thread.

Di solito useresti

$ echo "$FOO"

Tuttavia, ho avuto problemi anche con questa sintassi. Considera il seguente script.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

Le *necessità devono essere passate alla lettera curl, ma sorgeranno gli stessi problemi. L'esempio sopra non funzionerà (si espanderà ai nomi di file nella directory corrente) e nessuno dei due funzionerà \*. Inoltre non puoi citare $curl_optsperché verrà riconosciuto come una singola opzione (non valida) curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Pertanto, consiglierei l'uso della bashvariabile $GLOBIGNOREper impedire del tutto l'espansione del nome file se applicato al modello globale, o usare il set -fflag incorporato.

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

Applicando al tuo esempio originale:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

4
Grande spiegazione, grazie! Il mio caso d'uso è SELECT * FROM etc., questo è l'unico modo che funziona.
knutole,

1
Grazie per aver presentato la soluzione set -f!
Hacre,

5
FOO='BAR * BAR'
echo "$FOO"

Funziona, ma non è essenziale cambiare le virgolette singole sulla prima riga in virgolette doppie.
Finnw,

1
No, non lo è, ma l'abitudine di usare virgolette singole è preferibile quando includerai caratteri di shell speciali e non vuoi sostituirli.
tzot,


3

Potrebbe valere la pena prendere l'abitudine di usare printfpiuttosto che echodalla riga di comando.

In questo esempio non offre molti vantaggi, ma può essere più utile con output più complessi.

FOO="BAR * BAR"
printf %s "$FOO"

Perché all'inferno? printf è un processo separato (almeno, non integrato in bash) e l'uso di printf che dimostrate non ha alcun vantaggio rispetto all'eco.
ddaa,

2
printf è un bash incorporato e nella mia risposta ho detto "In questo esempio non offre molti vantaggi ma può essere più utile con output più complessi.
Dave Webb,
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.