Fondamentalement, il s’agit d’un problème de portabilité (et de fiabilité).
Initialement, echo
n’a accepté aucune option et n’a rien développé. Tout ce qu'il faisait était de sortir ses arguments séparés par un espace et terminés par un caractère de nouvelle ligne.
Maintenant, quelqu'un a pensé que ce serait bien si nous pouvions faire des choses comme echo "\n\t"
sortir des caractères de nouvelle ligne ou de tabulation, ou avoir une option pour ne pas afficher le dernier caractère de nouvelle ligne.
Ils ont alors pensé plus fort, mais au lieu d’ajouter cette fonctionnalité au shell (comme à l’ perl
intérieur de guillemets, cela \t
signifie en fait un caractère de tabulation), ils l’ont ajoutée echo
.
David Korn a pris conscience de l’erreur et a introduit une nouvelle forme de guillemets: $'...'
elle a ensuite été copiée par bash
et zsh
mais c’était beaucoup trop tard à cette époque.
Désormais, lorsqu'un UNIX standard echo
reçoit un argument contenant les deux caractères \
et t
, au lieu de les générer, il génère un caractère de tabulation. Et dès qu’il voit \c
un argument, il arrête la sortie (la nouvelle ligne finale n’est donc pas sortie non plus).
D'autres shells / fournisseurs / versions d'Unix ont choisi de le faire différemment: ils ont ajouté une -e
option permettant de développer les séquences d'échappement et une -n
option permettant de ne pas afficher le retour à la ligne final. Certains ont la -E
possibilité de désactiver les séquences d'échappement, d'autres -n
non mais -e
la liste des séquences d'échappement prises en charge par une echo
implémentation n'est pas nécessairement la même que celle prise en charge par une autre.
Sven Mascheck a une belle page qui montre l'étendue du problème .
Sur ces echo
implémentations qui prennent en charge les options, il n'y a généralement pas de soutien d'un --
pour marquer la fin des options (le echo
builtin de quelques obus non Bourne comme le font, et zsh soutient -
pour que , bien), de sorte que , par exemple, il est difficile de sortie "-n"
avec echo
en beaucoup d'obus.
Sur certains shells comme bash
¹ ou ksh93
² ou yash
( $ECHO_STYLE
variable), le comportement dépend même de la manière dont le shell a été compilé ou de l'environnement ( echo
le comportement de GNU changera également s'il $POSIXLY_CORRECT
est dans l'environnement et avec la version 4 , zsh
avec son bsd_echo
option, certains basés sur pdksh avec leur posix
option ou s’ils sont appelés comme sh
ou non). Donc, deux bash
echo
, même de la même version de, bash
ne se comportent pas de la même manière.
POSIX dit: si le premier argument est -n
ou si l'un des arguments contient des barres obliques inverses, le comportement n'est pas spécifié . bash
echo à cet égard n’est pas POSIX dans la mesure où, par exemple, echo -e
ne produit pas -e<newline>
comme POSIX le requiert. La spécification UNIX est plus stricte, elle interdit -n
et nécessite le développement de certaines séquences d'échappement, y compris \c
celle destinée à arrêter la sortie.
Ces spécifications ne viennent pas vraiment à la rescousse ici étant donné que de nombreuses implémentations ne sont pas conformes. Même certains systèmes certifiés tels que macOS 5 ne sont pas conformes.
Pour représenter réellement la réalité actuelle, POSIX devrait en réalité indiquer : si le premier argument correspond à l' ^-([eEn]*|-help|-version)$
expression rationnelle étendue ou si l'un des arguments contient des barres obliques inverses (ou des caractères dont le codage contient le codage du caractère de la barre oblique inversée comme α
dans les paramètres régionaux utilisant le jeu de caractères BIG5), le comportement non spécifié.
Globalement, vous ne savez pas ce qui echo "$var"
sera généré à moins que vous ne puissiez vous assurer qu'il $var
ne contient pas de caractère barre oblique inverse et ne commence pas par -
. La spécification POSIX nous dit effectivement d’utiliser printf
plutôt dans ce cas.
Cela signifie donc que vous ne pouvez pas utiliser echo
pour afficher des données non contrôlées. En d'autres termes, si vous écrivez un script et qu'il prend des entrées externes (de l'utilisateur sous forme d'arguments, ou des noms de fichiers du système de fichiers ...), vous ne pouvez pas utiliser echo
pour l'afficher.
C'est acceptable:
echo >&2 Invalid file.
Ce n'est pas:
echo >&2 "Invalid file: $file"
(Bien que cela fonctionne correctement avec certaines echo
implémentations (non compatibles UNIX) telles que bash
lorsque l' xpg_echo
option n'a pas été activée d'une manière ou d'une autre, comme au moment de la compilation ou via l'environnement).
file=$(echo "$var" | tr ' ' _)
n'est pas OK dans la plupart des implémentations (exceptions étant yash
avec ECHO_STYLE=raw
(la mise en garde que yash
« les variables s ne peuvent pas contenir des séquences arbitraires d'octets afin de ne pas les noms de fichiers arbitraires) et zsh
» s echo -E - "$var"
6 ).
printf
, d’autre part, est plus fiable, du moins quand il est limité à l’utilisation de base de echo
.
printf '%s\n' "$var"
Affiche le contenu de $var
suivi d'un caractère de nouvelle ligne, quel que soit le caractère qu'il peut contenir.
printf '%s' "$var"
Le produira sans le caractère de fin de ligne suivant.
Maintenant, il y a aussi des différences entre les printf
implémentations. POSIX spécifie un noyau de fonctionnalités, mais il existe ensuite de nombreuses extensions. Par exemple, certains prennent en charge %q
les arguments, mais la manière de procéder varie d’un shell à l’autre, certains \uxxxx
prenant en charge les caractères unicode. Le comportement varie printf '%10s\n' "$var"
dans les environnements locaux multi-octets, il existe au moins trois résultats différents pourprintf %b '\123'
Mais à la fin, si vous vous en tenez à l'ensemble des fonctionnalités POSIX printf
et que vous n'essayez rien de trop fantaisiste, vous ne rencontrerez aucun problème.
Mais rappelez-vous que le premier argument est le format, il ne devrait donc pas contenir de données variables / non contrôlées.
Un plus fiable echo
peut être mis en œuvre en utilisant printf
, comme:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
Le sous-shell (ce qui implique de générer un processus supplémentaire dans la plupart des implémentations de shell) peut être évité en utilisant local IFS
plusieurs shell, ou en l'écrivant comme ceci:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Remarques
1. comment bash
le echo
comportement peut être modifié.
Avec bash
, au moment de l'exécution, deux éléments contrôlent le comportement de echo
(à côté de la enable -n echo
redéfinition en echo
tant que fonction ou alias): l' xpg_echo
bash
option et le bash
mode posix. posix
Le mode peut être activé si bash
est appelé en tant que sh
ou si se POSIXLY_CORRECT
trouve dans l'environnement ou avec l' posix
option:
Comportement par défaut sur la plupart des systèmes:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
étend les séquences car UNIX requiert:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Il honore encore -n
et -e
(et -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Avec xpg_echo
et en mode POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
Cette fois, bash
est conforme à la fois à POSIX et à UNIX. Notez qu'en mode POSIX, il bash
n'est toujours pas conforme à POSIX car il ne sort pas -e
en:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Les valeurs par défaut pour xpg_echo et posix peuvent être définies lors de la compilation avec les options --enable-xpg-echo-default
et --enable-strict-posix-default
du configure
script. C'est typiquement ce que les versions récentes d'OS / X font pour construire leurs /bin/sh
. Pas de mise en œuvre / la distribution Unix / Linux dans leur esprit serait généralement faire pour /bin/bash
bien . En fait, ce n'est pas vrai, le produit /bin/bash
fourni par Oracle avec Solaris 11 (dans un package facultatif) semble être construit --enable-xpg-echo-default
(ce qui n'était pas le cas dans Solaris 10).
2. Comment ksh93
le echo
comportement peut être modifié.
Dans ksh93
, le fait d' echo
étendre les séquences d'échappement ou non et de reconnaître les options dépend du contenu des variables d'environnement $PATH
et / ou $_AST_FEATURES
.
S'il $PATH
contient un composant qui contient /5bin
ou /xpg
avant le composant /bin
ou /usr/bin
, il se comporte de la manière SysV / UNIX (développe les séquences, n'accepte pas les options). S'il trouve /ucb
ou /bsd
premier ou si $_AST_FEATURES
7 contient UNIVERSE = ucb
, alors il se comporte de la manière BSD 3 ( -e
pour permettre l'expansion, reconnaît -n
).
La valeur par défaut dépend du système, BSD sous Debian (voir le résultat builtin getconf; getconf UNIVERSE
dans les versions récentes de ksh93):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD pour echo -e?
La référence à BSD pour le traitement de l' -e
option est un peu trompeuse ici. La plupart de ces echo
comportements différents et incompatibles ont tous été introduits chez AT & T:
\n
, \0ooo
, \c
Dans le banc de travail du programmeur UNIX (basé sur Unix V6), et le reste ( \b
, \r
...) dans Unix System III Réf .
-n
sous Unix V7 (par Dennis Ritchie Ref )
-e
sous Unix V8 (par Dennis Ritchie Ref )
-E
elle-même provient peut-être initialement de bash
(CWRU / CWRU.chlog dans la version 1.13.5 mentionne l’ajout de Brian Fox le 1992-10-18, GNU le echo
copiant peu de temps après dans sh-utils-1.8 publié 10 jours plus tard)
Alors que le echo
builtin des sh
ou BSDs ont soutenu -e
depuis le jour où ils ont commencé à utiliser le shell Almquist pour elle au début des années 90, la version autonome echo
utilitaire à ce jour ne supporte pas là ( FreeBSDecho
ne supporte toujours pas -e
, même si elle prend en charge -n
comme Unix V7 (et aussi \c
mais seulement à la fin du dernier argument)).
La manipulation de -e
a été ajouté à ksh93
« s echo
quand dans le BSD univers dans la version ksh93r sorti en 2006 et peut être désactivé au moment de la compilation.
4. GNU echo changement de comportement en 8.31
Depuis coreutils 8.31 (et cette validation ), GNU echo
étend désormais les séquences d'échappement par défaut lorsque POSIXLY_CORRECT est dans l'environnement, afin de correspondre au comportement de bash -o posix -O xpg_echo
la commande echo
intégrée de s (voir le rapport de bogue ).
5. macOS echo
La plupart des versions de macOS ont reçu la certification UNIX de OpenGroup .
Leur version sh
intégrée echo
est conforme car elle est bash
(une très ancienne version) construite avec xpg_echo
activé par défaut, mais leur echo
utilitaire autonome ne l’est pas. env echo -n
ne sort rien au lieu de -n<newline>
, env echo '\n'
sort \n<newline>
au lieu de <newline><newline>
.
C’est /bin/echo
celui de FreeBSD qui supprime la sortie de nouvelle ligne si le premier argument est -n
ou (depuis 1995) si le dernier argument se termine par \c
, mais ne supporte aucune autre séquence de barre oblique inverse requise par UNIX, même \\
.
6. des echo
implémentations qui peuvent produire des données arbitraires telles quelles
À proprement parler, vous pourriez aussi compter que FreeBSD / macOS /bin/echo
ci-dessus (et non dans le shell de leur shell echo
) où zsh
s echo -E - "$var"
ou yash
's ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) pourrait être écrit:
/bin/echo "$var
\c"
Les mises en œuvre qui prennent en charge -E
et -n
(ou peuvent être configurées pour) peuvent également faire:
echo -nE "$var
"
Et zsh
« s echo -nE - "$var"
( printf %s "$var"
) peuvent être écrites
/bin/echo "$var\c"
7. _AST_FEATURES
et l'ASTUNIVERSE
Il _AST_FEATURES
n'est pas destiné à être manipulé directement, il est utilisé pour propager les paramètres de configuration AST lors de l'exécution de commandes. La configuration doit être effectuée via l' astgetconf()
API (non documentée) . À l’intérieur ksh93
, l’ getconf
intégré (activé avec builtin getconf
ou par appel command /opt/ast/bin/getconf
) est l’interface pourastgetconf()
Par exemple, vous feriez builtin getconf; getconf UNIVERSE = att
changer le UNIVERSE
réglage en att
(faisant echo
se comporter de manière SysV entre autres). Après cela, vous remarquerez que la $_AST_FEATURES
variable d’environnement contient UNIVERSE = att
.
echo -e
?