web-dev-qa-db-fra.com

Comment échapper au caractère générique / astérisque dans bash?

par exemple.

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

et en utilisant le caractère d'échappement "\":

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

Je fais évidemment quelque chose de stupide.

Comment puis-je obtenir la sortie "BAR * BAR"?

123
andyuk

Citer lors de la définition de $ FOO ne suffit pas. Vous devez également citer la référence de la variable:

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

REPONSE COURTE

Comme d’autres l’ont dit, vous devez toujours citer les variables pour éviter tout comportement étrange. Donc, utilisez echo "$ foo" dans au lieu de echo $ foo.

RÉPONSE LONGUE

Je pense que cet exemple mérite une explication plus approfondie, car il se passe plus de choses qu'il n'y paraît.

Je peux voir d'où vient votre confusion, car après avoir donné votre premier exemple, vous vous êtes probablement dit que Shell était en train de le faire:

  1. Expansion des paramètres
  2. Extension de nom de fichier

Donc, à partir de votre premier exemple:

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

Après l’extension du paramètre est équivalent à:

me$ echo BAR * BAR

Et après l'extension du nom de fichier est équivalent à:

me$ echo BAR file1 file2 file3 file4 BAR

Et si vous tapez juste echo BAR * BAR dans la ligne de commande, vous verrez qu’ils sont équivalents.

Donc vous avez probablement pensé "si j'échappe au *, je peux empêcher l'expansion du nom de fichier"

Donc, à partir de votre deuxième exemple:

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

Après le développement des paramètres doit être équivalent à:

me$ echo BAR \* BAR

Et après l'extension du nom de fichier devrait être équivalent à:

me$ echo BAR \* BAR

Et si vous essayez de saisir "echo BAR\* BAR" directement dans la ligne de commande, il affichera effectivement "BAR * BAR" car le développement du nom de fichier est empêché par l'échappement.

Alors pourquoi utiliser $ foo ne fonctionne pas?

C'est parce qu'il y a une troisième expansion qui a lieu - Enlèvement de devis. De la suppression manuelle des devis bash, vous obtenez:

Après les extensions précédentes, toutes les occurrences non citées des caractères "\", "" et "" qui ne résultaient pas de l’une des extensions ci-dessus sont supprimées.

Ainsi, lorsque vous tapez la commande directement dans la ligne de commande, le caractère d'échappement n'est pas le résultat d'une extension précédente. BASH le supprime avant de l'envoyer à la commande echo. Dans le deuxième exemple, le caractère "\ *" était le résultat d'une extension de paramètre précédente, il n'est donc PAS supprimé. En conséquence, l'écho reçoit "\ *" et c'est ce qu'il affiche.

Notez la différence entre le premier exemple - "*" n'est pas inclus dans les caractères qui seront supprimés par Suppression de devis.

J'espère que cela a du sens. En fin de compte la conclusion dans le même - il suffit d'utiliser des guillemets. Je pensais juste que j'expliquerais pourquoi échapper, ce qui devrait logiquement fonctionner si seuls les extensions Paramètre et Nom de fichier sont en jeu, ne fonctionnait pas.

Pour une explication complète des extensions BASH, reportez-vous à:

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

96
mithu

Je vais ajouter un peu à ce vieux fil.

Habituellement, vous utiliseriez

$ echo "$FOO"

Cependant, j'ai eu des problèmes même avec cette syntaxe. Considérez le script suivant.

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

Le * Doit être transmis textuellement à curl, mais les mêmes problèmes se poseront. L'exemple ci-dessus ne fonctionnera pas (il développera les noms de fichiers du répertoire actuel) et \* Non plus. Vous ne pouvez pas non plus citer $curl_opts, Car elle sera reconnue comme une option unique (non valide) pour curl.

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

Par conséquent, je recommanderais l'utilisation de la variable bash$GLOBIGNORE Pour empêcher l'expansion du nom de fichier si elle est appliquée au modèle global, ou utiliser l'indicateur intégré set -f.

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

En appliquant votre exemple original:

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
47
AlexandreH
FOO='BAR * BAR'
echo "$FOO"
4
tzot

Il peut être intéressant de prendre l’habitude d’utiliser printf plutôt que echo sur la ligne de commande.

Dans cet exemple, cela ne présente pas beaucoup d'avantages, mais cela peut être plus utile avec des sorties plus complexes.

FOO="BAR * BAR"
printf %s "$FOO"
3
Dave Webb
echo "$FOO"
3
Rafał Dowgird