J'ai entendu dire que printf
vaut mieux que echo
. Je ne me souviens que d'un seul cas de mon expérience où j'ai dû utiliser printf
parce que echo
n'a pas fonctionné pour alimenter du texte dans un programme sur RHEL 5.8 mais printf
a fait. Mais apparemment, il existe d'autres différences, et j'aimerais savoir quelles sont-elles ainsi que s'il existe des cas spécifiques dans lesquels utiliser l'une par rapport à l'autre.
Réponse acceptée :
Fondamentalement, c'est 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 "nt"
pour afficher les caractères de nouvelle ligne ou de tabulation, ou avoir la possibilité de ne pas afficher le caractère de fin de ligne.
Ils ont ensuite réfléchi plus sérieusement mais au lieu d'ajouter cette fonctionnalité au shell (comme perl
où entre guillemets doubles, t
signifie en fait un caractère de tabulation), ils l'ont ajouté à echo
.
David Korn s'est rendu compte de l'erreur et a introduit une nouvelle forme de guillemets :$'...'
qui a ensuite été copié par bash
et zsh
mais il était bien trop tard à ce moment-là.
Maintenant, lorsqu'un echo
UNIX standard reçoit un argument qui contient les deux caractères et
t
, au lieu de les sortir, il sort un caractère de tabulation. Et dès qu'il voit c
dans un argument, il arrête de sortir (donc la nouvelle ligne de fin n'est pas sortie non plus).
D'autres shells/éditeurs/versions Unix ont choisi de le faire différemment :ils ont ajouté un -e
option pour développer les séquences d'échappement, et un -n
option pour ne pas afficher la nouvelle ligne de fin. Certains ont un -E
pour désactiver les séquences d'échappement, certains ont -n
mais pas -e
, la liste des séquences d'échappement supportées par un echo
la mise en œuvre n'est pas nécessairement la même que celle prise en charge par un 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 prise en charge d'un --
pour marquer la fin des options (le echo
certains shells non similaires à Bourne le font, et zsh prend en charge -
pour cela cependant), donc par exemple, il est difficile de sortir "-n"
avec echo
dans de nombreux coquillages.
Sur certains shells comme bash
¹ ou ksh93
² ou yash
($ECHO_STYLE
variable), le comportement dépend même de la façon dont le shell a été compilé ou de l'environnement (GNU echo
Le comportement de va également changer si $POSIXLY_CORRECT
est dans l'environnement et avec la version, zsh
's avec son bsd_echo
option, certains basés sur pdksh avec leur posix
option ou s'ils sont appelés comme sh
ou pas). Donc deux bash
echo
s, même à partir de la même version de bash
ne sont pas garantis de se comporter de la même manière.
POSIX dit :si le premier argument est -n
ou tout argument contient des barres obliques inverses, alors le comportement n'est pas spécifié . bash
echo à cet égard n'est pas POSIX dans la mesure où par exemple echo -e
n'affiche pas -e<newline>
comme POSIX l'exige. La spécification UNIX est plus stricte, elle interdit -n
et nécessite l'expansion de certaines séquences d'échappement, y compris le c
un pour 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 certifiés les systèmes comme macOS ne sont pas compatibles.
Pour vraiment représenter la réalité actuelle, POSIX devrait en fait dire :si le premier argument correspond au ^-([eEn]*|-help|-version)$
une expression rationnelle étendue ou tout argument contient des barres obliques inverses (ou des caractères dont l'encodage contient l'encodage du caractère barre oblique inverse comme α
dans les locales utilisant le jeu de caractères BIG5), alors le comportement n'est pas spécifié.
Dans l'ensemble, vous ne savez pas ce que echo "$var"
sortira sauf si vous pouvez vous assurer que $var
ne contient pas de barres obliques inverses et ne commence pas par -
. La spécification POSIX nous dit en fait d'utiliser printf
à la place dans ce cas.
Donc, cela signifie 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 une entrée externe (de l'utilisateur comme arguments, ou des noms de fichiers du système de fichiers…), vous ne pouvez pas utiliser echo
pour l'afficher.
C'est OK :
echo >&2 Invalid file.
Ce n'est pas :
echo >&2 "Invalid file: $file"
(Bien que cela fonctionnera bien avec certains echo
(non conformes à UNIX) implémentations comme bash
c'est quand le xpg_echo
l'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 (les exceptions étant yash
avec ECHO_STYLE=raw
(avec la mise en garde que yash
Les variables de 's ne peuvent pas contenir de séquences arbitraires d'octets, donc pas de noms de fichiers arbitraires) et zsh
echo -E - "$var"
).
printf
, en revanche est plus fiable, du moins lorsqu'il se limite à l'utilisation basique de echo
.
printf '%sn' "$var"
Affichera le contenu de $var
suivi d'un caractère de saut de ligne, quel que soit le caractère qu'il peut contenir.
printf '%s' "$var"
Le sortira sans le caractère de fin de ligne.
Maintenant, il y a aussi des différences entre printf
implémentations. Il y a un noyau de fonctionnalités qui est spécifié par POSIX, mais il y a aussi beaucoup d'extensions. Par exemple, certains supportent un %q
pour citer les arguments, mais la façon dont c'est fait varie d'un shell à l'autre, certains prennent en charge uxxxx
pour les caractères unicode. Le comportement varie pour printf '%10sn' "$var"
dans les locales multi-octets, il y a au moins trois résultats différents pour printf %b '123'
Mais au final, si vous vous en tenez à l'ensemble de fonctionnalités POSIX de printf
et n'essayez pas de faire quelque chose de trop fantaisiste avec, vous n'aurez plus d'ennuis.
Mais rappelez-vous que le premier argument est le format, il ne doit donc pas contenir de données variables/incontrôlées.
Un echo
plus fiable peut être implémenté en utilisant printf
, comme :
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
Le sous-shell (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
avec plusieurs coques, ou en l'écrivant comme :
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "[email protected]"
fi
fi
printf 'n'
}
Remarques
1. comment bash
echo
le comportement peut être modifié.
Avec bash
, au moment de l'exécution, il y a deux choses qui contrôlent le comportement de echo
(à côté de enable -n echo
ou redéfinir echo
en tant que fonction ou alias) :
le xpg_echo
bash
option et si bash
est en mode posix. posix
le mode peut être activé si bash
s'appelle sh
ou si POSIXLY_CORRECT
est dans l'environnement ou avec le posix
choix :
Comportement par défaut sur la plupart des systèmes :
$ bash -c 'echo -n "