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:
Heredocs:
for reps in 1 2 3 do time for i in {1..1000} do cat <<'END' test ssortingng END done > /dev/null done real 0m1.786s user 0m0.212s sys 0m0.332s real 0m1.817s user 0m0.232s sys 0m0.332s real 0m1.846s user 0m0.256s sys 0m0.320s
Heressortingngs
for reps in 1 2 3 do time for i in {1..1000} do cat <<< 'test ssortingng' done > /dev/null done real 0m1.932s user 0m0.280s sys 0m0.288s real 0m1.956s user 0m0.248s sys 0m0.348s real 0m1.968s user 0m0.268s sys 0m0.324s
Redirection
for reps in 1 2 3 do time for i in {1..1000} do echo 'test ssortingng' | cat done > /dev/null done real 0m3.562s user 0m0.416s sys 0m0.548s real 0m3.924s user 0m0.384s sys 0m0.604s real 0m3.343s user 0m0.400s sys 0m0.552s
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:
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.) 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.