Quelle est la différence entre l'écho «date», l'écho «« date »» et l'écho «« date »»?

23

Quelle est la différence entre ces trois commandes?

echo `date`
echo "`date`"
echo '`date`'

Je suis confus sur les différences réelles. Je pense que lorsque le 'est autour, cela signifie qu'il s'agit d'une chaîne, donc l'écho afficherait littéralement la chaîne dateau lieu d'afficher la date?

John
la source

Réponses:

19

`date` s'étendra simplement à la sortie de la datecommande. Cependant, il supprime les caractères d'espace supplémentaires aux endroits où il y a plus d'un caractère d'espace consécutif dans la sortie. (Cela est dû au fait que la substitution de commande est sujette au fractionnement de mots et à la façon dont la echocommande gère plusieurs arguments.)

Dans "` date` " , les guillemets doubles sont des guillemets faibles, ils étendent donc les variables (essayez" $ PWD ") et effectuent la substitution de commande. Le résultat de l'expansion est transmis en tant qu'argument unique à la echocommande, avec tous les espaces consécutifs inclus: autrement dit, le fractionnement de mot n'est pas effectué.

Dans 'date' , les guillemets simples sont des guillemets plus forts, donc ils ne permettront pas l'expansion des variables ou la substitution de commandes en leur sein.

Reportez - vous à ce lien pour plus d'explications.

Modifié le premier point comme l'a correctement souligné Michael Suelmann dans le commentaire ci-dessous .

Chirag Bhatia - chirag64
la source
1
Comment s'appelle exactement le `caractère qui entoure la date? Si je comprends bien, les méta-caractères ne fonctionneront pas dans les guillemets simples?
John
Si je comprends bien, les méta-caractères ne fonctionneront pas dans les guillemets simples?
John
8
Le `est souvent appelé un" backtick "dans ce scénario et divers documents / livres Unix. Ce n'est pas réellement utilisé comme un accent grave Unicode quand il est comme ça, même si c'est le nom du symbole. Et vous avez raison, aucune expansion des métacaractères / expressions ne se produira si vous l'entourez de guillemets simples.
Jim Stewart
Votre première déclaration est incorrecte car la sortie peut être légèrement différente selon la date ou les paramètres régionaux. Seule la deuxième commande produira la même chose que la datecommande nue .
jlliagre
1
@BonsiScott L'espace supplémentaire entre "Nov" et "1" est également supprimé en HTML;)
Izkata
16

Tous les deux

echo `date`

et

echo "`date`"

affichera la date. La sortie de ce dernier ressemble à la sortie de l'exécution datepar elle-même.

Il y a cependant une différence: celui entouré de "guillemets "sera envoyé à en echotant qu'argument unique. Les guillemets encapsulent la sortie de la commande entière en un seul argument. Comme echoil affiche simplement ses arguments dans l'ordre, avec des espaces entre les deux, il sera fondamentalement le même.

Voici un exemple de la subtile différence:

echo `date`

produit:

Fri Nov 1 01:48:45 EST 2013

mais:

echo "`date`"

produit:

Fri Nov  1 01:48:49 EST 2013

Notez que les deux espaces après Novont été réduits à un sans les guillemets. En effet, le shell analyse chaque élément séparé par des espaces et envoie le résultat à l'écho sous forme de 6 arguments. Lorsque vous le citez, echo reçoit un seul argument et les guillemets conservent l'espace.

Cela devient beaucoup plus important dans les commandes autres que l'écho. Par exemple, imaginez une commande fooqui souhaite deux arguments: une date et une adresse e-mail.

Cela fonctionnera dans ce scénario:

foo "`date`" joeuser@example.com

Mais cela confondra le script en lui envoyant 7 arguments:

foo `date` joeuser@example.com
Jim Stewart
la source
3
Vous vous contredisez dans votre première phrase. Le premier et le deuxième formulaire ne produiront pas toujours la même chose que vous montrez plus tard.
jlliagre
Merci. J'ai modifié le libellé pour que ce soit plus clair.
Jim Stewart
3

Dans les shells POSIX, `date`c'est l'ancienne forme de substitution de commande. La syntaxe moderne est $(date).

Dans les deux cas, ils se développent jusqu'à la sortie de dateavec les caractères de fin de ligne supprimés (à condition que la sortie ne contienne pas de caractères NUL).

Cependant, lorsqu'il n'est pas entre guillemets doubles et dans des contextes de liste (par exemple dans les arguments de commandes simples comme echodans votre cas), cette expansion est en outre soumise à:

  1. Fractionnement de mots : c'est-à-dire que la "sortie de dateavec les caractères de nouvelle ligne de fin supprimés" est divisée en fonction de la valeur actuelle de la $IFSvariable (contenant par défaut espace, tabulation et nouvelle ligne (et NUL avec zsh)) en plusieurs mots .

    Par exemple, si les datesorties Fri 1 Nov 14:11:15 GMT 2013\n(comme il le fait souvent dans un environnement linguistique anglais et dans un fuseau horaire continent britannique), et $IFScontient actuellement :, qui sera divisé en 3 mots : Fri 1 Nov 14, 11et 15 GMT 2013.

  2. Génération Nom du fichier (aka englobement ) (sauf zsh): qui est, chaque mot résultant de la division ci - dessus est recherché des caractères génériques ( *, ?, [...]bien que certains obus ont plus), et étendu à la liste des noms de fichiers qui correspondent à ces modèles. Par exemple, si la sortie dateest ?%? 33 */*/* UVC 3432(comme il est souvent dans des endroits vénusienne et fuseau horaire UVC), et $IFSest la valeur par défaut), puis qui se développe à tous les noms de fichiers de caractères non-cachés 3 dans le répertoire courant dont le caractère milieu est %, 33, tous les fichiers non masqués dans tous les sous-répertoires non masqués de tous les sous-répertoires non masqués du répertoire en cours, UVCet 3432.

C'est pourquoi:

  1. Vous devez toujours citer (avec des guillemets doubles) les substitutions de commande, sauf si vous souhaitez que le fractionnement du mot ou la génération du nom de fichier soit effectué lors de son expansion.
  2. Si vous souhaitez séparer les mots , vous devez définir $IFSles caractères sur lesquels vous souhaitez fractionner.
  3. Si vous voulez le fractionnement de mots mais pas la génération de nom de fichier , vous devez émettre un set +fpour le désactiver.

Les guillemets simples citent tout, donc les caractères de backtick doivent être pris littéralement.

Exemple (l'utilisation -xpermet de voir plus facilement ce qui se passe):

$ bash --norc -x
bash-4.2$ IFS=:
+ IFS=:
bash-4.2$ echo `date`
++ date
+ echo 'Fri  1 Nov 14' 42 '33 GMT 2013'
Fri  1 Nov 14 42 33 GMT 2013
bash-4.2$ echo "`date`"
++ date
+ echo 'Fri  1 Nov 14:42:41 GMT 2013'
Fri  1 Nov 14:42:41 GMT 2013

bash-4.2$ cd /lib/modules
+ cd /lib/modules
bash-4.2$ export TZ=UVC LC_ALL=vs_VS
+ export TZ=UVC LC_ALL=vs_VS
+ TZ=UVC
+ LC_ALL=vs_VS
bash-4.2$ unset -v IFS     # get the default behaviour
+ unset -v IFS
bash-4.2$ echo `date`
++ date
+ echo '?%?' 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
?%? 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
bash-4.2$ echo "`date`"
++ date
+ echo '?%? 33 */*/* UVC 3432'
?%? 33 */*/* UVC 3432

Si la sortie contient des caractères NUL, le comportement varie d'un shell à l'autre: certains les suppriment, certains tronquent la sortie au premier caractère NUL, les zshconservent mais notez que de toute façon les commandes externes ne peuvent pas prendre d'arguments contenant des NUL

Stéphane Chazelas
la source
Il n'existe pas de véritable "shell POSIX". Il existe des shells qui peuvent se conformer aux différentes normes POSIX pertinentes en utilisant un sous-ensemble limité de leurs fonctionnalités.
fpmurphy
0

Avec `date`, vous obtenez la sortie de la date divisée en plusieurs mots, car la division des mots est effectuée après la substitution de commande.

Avec "` date` ", vous obtenez la sortie de la date sous la forme d'un mot / paramètre car il existe une substitution de commande entre guillemets doubles, mais la sortie n'est pas analysée davantage. La même chose est valable avec une expansion variable comme "$ i" dans mon exemple ci-dessous.

Avec «date», vous obtenez une «date» littérale car il n'y a pas de substitution de commande entre guillemets simples.

Peut-être que les différences des 3 formes seront plus visibles de cette façon:

> for i in `date`; do echo "$i"; done
Fr
1.
Nov
12:25:30
CET
2013

> for i in "`date`"; do echo "$i"; done
Fr 1. Nov 12:25:38 CET 2013

> for i in '`date`'; do echo "$i"; done
`date`
Michael Suelmann
la source