Fondamentalement, il s’agit d’un problème de portabilité (et de fiabilité).
Initialement, echon’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’ perlintérieur de guillemets, cela \tsignifie 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 bashet zshmais c’était beaucoup trop tard à cette époque.
Désormais, lorsqu'un UNIX standard echoreç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 \cun 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 -eoption permettant de développer les séquences d'échappement et une -noption permettant de ne pas afficher le retour à la ligne final. Certains ont la -Epossibilité de désactiver les séquences d'échappement, d'autres -nnon mais -ela liste des séquences d'échappement prises en charge par une echoimplé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 echoimplé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 echobuiltin 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 echoen beaucoup d'obus.
Sur certains shells comme bash¹ ou ksh93² ou yash( $ECHO_STYLEvariable), le comportement dépend même de la manière dont le shell a été compilé ou de l'environnement ( echole comportement de GNU changera également s'il $POSIXLY_CORRECTest dans l'environnement et avec la version 4 , zshavec son bsd_echooption, certains basés sur pdksh avec leur posixoption ou s’ils sont appelés comme shou non). Donc, deux bash echo, même de la même version de, bashne se comportent pas de la même manière.
POSIX dit: si le premier argument est -nou si l'un des arguments contient des barres obliques inverses, le comportement n'est pas spécifié . bashecho à cet égard n’est pas POSIX dans la mesure où, par exemple, echo -ene produit pas -e<newline>comme POSIX le requiert. La spécification UNIX est plus stricte, elle interdit -net nécessite le développement de certaines séquences d'échappement, y compris \ccelle 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 $varne contient pas de caractère barre oblique inverse et ne commence pas par -. La spécification POSIX nous dit effectivement d’utiliser printfplutôt dans ce cas.
Cela signifie donc que vous ne pouvez pas utiliser echopour 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 echopour 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 echoimplémentations (non compatibles UNIX) telles que bashlorsque l' xpg_echooption 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 yashavec 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 $varsuivi 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 printfimplémentations. POSIX spécifie un noyau de fonctionnalités, mais il existe ensuite de nombreuses extensions. Par exemple, certains prennent en charge %qles arguments, mais la manière de procéder varie d’un shell à l’autre, certains \uxxxxprenant 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 printfet 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 echopeut ê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 IFSplusieurs 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 bashle echocomportement 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 echoredéfinition en echotant que fonction ou alias): l' xpg_echo bashoption et le bashmode posix. posixLe mode peut être activé si bashest appelé en tant que shou si se POSIXLY_CORRECTtrouve dans l'environnement ou avec l' posixoption:
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 -net -e(et -E):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Avec xpg_echoet 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, bashest conforme à la fois à POSIX et à UNIX. Notez qu'en mode POSIX, il bashn'est toujours pas conforme à POSIX car il ne sort pas -een:
$ 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-defaultet --enable-strict-posix-defaultdu configurescript. 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/bashbien . En fait, ce n'est pas vrai, le produit /bin/bashfourni 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 ksh93le echocomportement 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 $PATHet / ou $_AST_FEATURES.
S'il $PATHcontient un composant qui contient /5binou /xpgavant le composant /binou /usr/bin, il se comporte de la manière SysV / UNIX (développe les séquences, n'accepte pas les options). S'il trouve /ucbou /bsdpremier ou si $_AST_FEATURES7 contient UNIVERSE = ucb, alors il se comporte de la manière BSD 3 ( -epour 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 UNIVERSEdans 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' -eoption est un peu trompeuse ici. La plupart de ces echocomportements différents et incompatibles ont tous été introduits chez AT & T:
\n, \0ooo, \cDans le banc de travail du programmeur UNIX (basé sur Unix V6), et le reste ( \b, \r...) dans Unix System III Réf .
-nsous Unix V7 (par Dennis Ritchie Ref )
-esous Unix V8 (par Dennis Ritchie Ref )
-Eelle-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 echocopiant peu de temps après dans sh-utils-1.8 publié 10 jours plus tard)
Alors que le echobuiltin des shou BSDs ont soutenu -edepuis le jour où ils ont commencé à utiliser le shell Almquist pour elle au début des années 90, la version autonome echoutilitaire à ce jour ne supporte pas là ( FreeBSDecho ne supporte toujours pas -e, même si elle prend en charge -ncomme Unix V7 (et aussi \cmais seulement à la fin du dernier argument)).
La manipulation de -ea été ajouté à ksh93« s echoquand 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_echola commande echointé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 shintégrée echoest conforme car elle est bash(une très ancienne version) construite avec xpg_echoactivé par défaut, mais leur echoutilitaire autonome ne l’est pas. env echo -nne sort rien au lieu de -n<newline>, env echo '\n'sort \n<newline>au lieu de <newline><newline>.
C’est /bin/echocelui de FreeBSD qui supprime la sortie de nouvelle ligne si le premier argument est -nou (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 echoimplémentations qui peuvent produire des données arbitraires telles quelles
À proprement parler, vous pourriez aussi compter que FreeBSD / macOS /bin/echoci-dessus (et non dans le shell de leur shell echo) où zshs 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 -Eet -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_FEATURESet l'ASTUNIVERSE
Il _AST_FEATURESn'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’ getconfintégré (activé avec builtin getconfou par appel command /opt/ast/bin/getconf) est l’interface pourastgetconf()
Par exemple, vous feriez builtin getconf; getconf UNIVERSE = attchanger le UNIVERSEréglage en att(faisant echose comporter de manière SysV entre autres). Après cela, vous remarquerez que la $_AST_FEATURESvariable d’environnement contient UNIVERSE = att.
echo -e?