echo vs <<<, ou utilisation inutile de l'écho dans le prix Bash?

A présent, l' utilisation inutile de la récompense de cat est très bien connue, et il est également fait mention d'une utilisation inutile de l' echo (non pertinent pour cette question). Je me request s'il devrait y avoir une «utilisation inutile de l' echo dans le prix Bash»: la tuyauterie semble être beaucoup plus lente que les heredocs et les heressortingngs selon certaines mesures hautement non scientifiques:

En général, les heredocs et les heressortingngs sont à peu près la même vitesse (il s'agit d'un set de données issu de plusieurs tests), tandis que la redirection est toujours plus lente que 50%. Ai-je mal compris quelque chose, ou est-ce que cela pourrait être utilisé comme règle générale pour les commands de lecture d'input standard dans Bash?

Tout d'abord, concentrons-nous sur la performance. J'ai couru des benchmarks pour un programme légèrement différent sur un processeur x86_64 normalement inactif fonctionnant avec Debian squeeze.

heressortingng.bash , en utilisant un heressortingng pour passer une ligne d'input:

 #! /bin/bash i=0 while [ $i -lt $1 ]; do tr az AZ <<<'hello world' i=$((i+1)) done >/dev/null 

heredoc.bash , en utilisant un heredoc pour passer une ligne d'input:

 #! /bin/bash i=0 while [ $i -lt $1 ]; do tr az AZ <<'EOF' hello world EOF i=$((i+1)) done >/dev/null 

echo.bash , en utilisant echo et un tube pour passer une ligne d'input:

 #! /bin/bash i=0 while [ $i -lt $1 ]; do echo 'hello world' | tr az AZ i=$((i+1)) done >/dev/null 

A titre de comparaison, j'ai également chronométré les scripts sous ATT ksh93 et ​​sous tiret (sauf pour heressortingng.bash , car dash n'a pas de heressortingngs).

Voici médiane de trois fois:

 $ time bash ./heressortingng.bash 10000 ./heressortingng.bash 10000 0.32s user 0.79s system 15% cpu 7.088 total $ time ksh ./heressortingng.bash 10000 ksh ./heressortingng.bash 10000 0.54s user 0.41s system 17% cpu 5.277 total $ time bash ./heredoc.bash 10000 ./heredoc.bash 10000 0.35s user 0.75s system 17% cpu 6.406 total $ time ksh ./heredoc.bash 10000 ksh ./heredoc.sh 10000 0.54s user 0.44s system 19% cpu 4.925 total $ time sh ./heredoc.bash 10000 ./heredoc.sh 10000 0.08s user 0.58s system 12% cpu 5.313 total $ time bash ./echo.bash 10000 ./echo.bash 10000 0.36s user 1.40s system 20% cpu 8.641 total $ time ksh ./echo.bash 10000 ksh ./echo.sh 10000 0.47s user 1.51s system 28% cpu 6.918 total $ time sh ./echo.sh 10000 ./echo.sh 10000 0.07s user 1.00s system 16% cpu 6.463 total 

Conclusions:

  • Un heredoc est plus rapide qu'un heressortingng.
  • echo et un tuyau est sensiblement, mais pas beaucoup plus rapide. (Gardez à l'esprit qu'il s'agit d'un programme de jouets: dans un programme réel, la plus grande partie du time de traitement serait dans ce que l'appel tr représente ici.)
  • Si vous voulez la vitesse, fossé bash et appel tiret ou même mieux ksh à la place. Les caractéristiques de Bash ne compensent pas sa lenteur relative, mais ksh a à la fois des caractéristiques et de la vitesse.

Au-delà de la performance, il y a aussi la clarté et la portabilité. <<< est une extension ksh93 / bash / zsh qui est less connue que l' echo … | ou << . Cela ne fonctionne pas dans ksh88 / pdksh ou dans POSIX sh.

Le seul endroit où <<< est sans doute significativement plus clair est à l'intérieur d'un heredoc:

 foo=$(tr az AZ <<<'hello world') 

contre

 foo=$(tr az AZ <<'EOF' hello world EOF ) 

(La plupart des shells ne peuvent pas faire face à la fermeture de la parenthèse à la fin de la ligne contenant <<EOF .)

Une autre raison d'utiliser heredocs (si vous n'en aviez pas assez) est que l'écho peut échouer si le stream n'est pas consommé. Envisagez d'avoir l'option bash ' pipefail :

 set -o pipefail foo=yawn echo $foo | /bin/true ; echo $? # returns 0 

/bin/true ne consum pas son input standard, mais le echo yawn termine néanless. Cependant, si l'on request à echo d'imprimer un grand nombre de données, il ne sera pas terminé avant que true soit terminé:

 foo=$(cat /etc/passwd) # foo now has a fair amount of data echo $foo | /bin/true ; echo $? # returns 0 sometimes 141 echo $foo$foo$foo$foo | /bin/true ; echo $? # returns mostly 141 

141 est SIGPIPE (128 + 13) (128 étant ajouté parce que bash le fait selon bash (1):

Lorsqu'une command se termine par un signal fatal N, bash utilise la valeur 128 + N comme état de sortie.

Heredocs n'a pas ce problème:

 /bin/true <<< $foo$foo$foo$foo ; echo $? # returns 0 always 

Une des raisons pour lesquelles vous pourriez vouloir utiliser echo est d'exhiner un peu de contrôle sur le caractère newline ajouté à la fin de heredocs et heressortingngs:

Trois caractères foo a une longueur de 3:

 $ echo -n foo | wc -c 3 

Cependant, un troisième personnage comporte quatre caractères:

 $ wc -c <<< foo 4 

Un heredoc à trois caractères aussi:

 $ wc -c << EOF foo EOF 4 

Le quasortingème caractère est un caractère 0x0a .

D'une manière ou d'une autre, cela correspond magiquement à la façon dont bash supprime ces caractères de nouvelle ligne lors de l'extraction de la sortie d'un sous-shell:

Voici une command qui renvoie quatre caractères: foo et \n . Le '\ n' est ajouté par echo, il ajoute toujours un caractère de nouvelle ligne sauf si vous spécifiez l'option -n :

 $ echo foo foo $ echo foo | wc -c 4 

Toutefois, en l'affectant à une variable, la nouvelle ligne de fin ajoutée par echo est supprimée:

 $ foo=$(echo foo) $ echo "$foo" # here, echo adds a newline too. foo 

Donc, si vous mélangez des files et des variables et que vous les utilisez dans des calculs (par exemple, vous ne pouvez pas utiliser heredocs ou heressortingngs, car ils appendont une nouvelle ligne.

 foo=abc echo -n 'abc' > something.txt if [ $(wc -c <<< "$foo") -eq $(wc -c < something.txt) ] ; then echo "yeah, they're the same!" else echo "foo and bar have different lengths (except, maybe not)" fi 

Si vous changez l'instruction if pour lire

 if [ $(echo -n "$foo" | wc -c) -eq $(wc -c < something.txt) ] ; then 

alors le test passe.