echo vs ou Useless Use of echo no Bash Award?

14

Até o momento, o Uso inútil do cat Award é muito conhecido, e há também uma menção a um Uso inútil de echo (não relevante para essa pergunta). Eu estou querendo saber se deveria haver um "uso inútil de echo no Prêmio Bash": Piping parece ser muito mais lento que heredocs e herestrings de acordo com algumas medidas altamente não-científicas:

  • Heredocs:

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            cat <<'END'
    test string
    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
    
  • Herestrings

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            cat <<< 'test string'
        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
    
  • Redirecionamento

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            echo 'test string' | 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
    

Em geral, heredocs e herestrings têm aproximadamente a mesma velocidade (esse é apenas um conjunto de dados de vários testes), enquanto o redirecionamento é consistentemente mais de 50% mais lento. Eu estou entendendo mal alguma coisa, ou isso poderia ser usado como uma regra geral para comandos de leitura de entrada padrão no Bash?

    
por l0b0 20.12.2012 / 14:40

3 respostas

9

Primeiro, vamos nos concentrar no desempenho. Eu executei benchmarks para um programa ligeiramente diferente em um processador x86_64 de outra forma ocioso, rodando Debian squeeze.

herestring.bash , usando um heráldico para passar uma linha de entrada:

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

heredoc.bash , usando um heredoc para passar uma linha de entrada:

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

echo.bash , usando echo e um pipe para passar uma linha de entrada:

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

Para comparação, também sincronizei os scripts em ATT ksh93 e under dash (exceto por herestring.bash , porque dash não possui herestrings).

Aqui estão uma média de três vezes:

$ time bash ./herestring.bash 10000
./herestring.bash 10000  0.32s user 0.79s system 15% cpu 7.088 total
$ time ksh ./herestring.bash 10000
ksh ./herestring.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

Conclusões:

  • Um heredoc é mais rápido que um heredring.
  • echo e um canal é notavelmente, mas não dramaticamente mais rápido. (Lembre-se de que se trata de um programa de brinquedo: em um programa real, a maior parte do tempo de processamento estaria em qualquer que seja a tr chamada aqui.)
  • Se você quiser velocidade, valide bash e call dash ou melhor ainda ksh. Os recursos do Bash não compensam sua relativa lentidão, mas o ksh tem recursos e velocidade.

Além do desempenho, também há clareza e portabilidade. <<< é uma extensão ksh93 / bash / zsh que é menos conhecida que echo … | ou << . Não funciona no ksh88 / pdksh ou no POSIX sh.

O único local em que <<< é significativamente mais claro está dentro de um heredoc:

foo=$(tr a-z A-Z <<<'hello world')

vs

foo=$(tr a-z A-Z <<'EOF'
hello world
EOF
)

(A maioria dos shells não consegue lidar com o fechamento dos parênteses no final da linha contendo <<EOF .)

    
por 21.12.2012 / 02:38
6

Outro motivo para usar heredocs (se você não tem o suficiente) é que o eco pode falhar se o fluxo não for consumido. Considere ter o bash ' pipefail option:

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

/bin/true não consome sua entrada padrão, mas echo yawn conclui, no entanto. No entanto, se for solicitado que o eco imprima muitos dados, ele não será concluído até que true seja concluído:

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 é SIGPIPE (128 + 13) (128 sendo adicionado porque bash faz isso de acordo com bash (1):

When a command terminates on a fatal signal N, bash uses the value of 128+N as the exit status.

Heredocs não têm esse problema:

/bin/true <<< $foo$foo$foo$foo ; echo $?  # returns 0 always
    
por 15.10.2013 / 13:32
1

Um motivo pelo qual você pode querer usar o echo é aumentar o controle do caractere de nova linha que é adicionado ao final de heredocs e herestrings:

Três caracteres foo tem comprimento 3:

$ echo -n foo | wc -c
3

No entanto, a herança de três caracteres é de quatro caracteres:

$ wc -c <<< foo
4

Um heredoc de três caracteres também:

$ wc -c << EOF
foo
EOF
4

O quarto caractere é um caractere 0x0a de nova linha.

De alguma forma isso se encaixa magicamente na forma como o bash remove esses caracteres de nova linha ao capturar a saída de um sub-shell:

Aqui está um comando que retorna quatro caracteres: foo e \n . O '\ n' é adicionado pelo echo, ele sempre adiciona um caractere de nova linha, a menos que você especifique a opção -n :

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

No entanto, atribuindo isso a uma variável, a nova linha final adicionada pelo echo é removida:

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

Então, se você misturar arquivos e variáveis e usá-los em cálculos (por exemplo, não é possível usar heredocs ou herestrings, pois eles adicionarão uma nova linha.

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

Se você alterar a instrução if para ler

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

o teste passa.

    
por 30.08.2017 / 14:27