echo vs <<<、またはBash Awardでのechoの無駄な使用?


18

今でcat無用使用は非常によく知られており、無用の使用echoについても言及されています(この質問には関係ありません)。「echoバッシュインアワードの役に立たない使用」があるべきかどうか疑問に思っています:非常に非科学的な測定によると、配管はヒアドキュメントとヒアストリングよりもはるかに遅いようです:

  • ヒアドキュメント:

    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
  • ヒアストリングス

    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
  • リダイレクション

    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

一般に、heredocsとherestringsはほぼ同じ速度です(これは複数のテストからの1つのデータセットにすぎません)が、リダイレクトは一貫して50%以上遅くなります。私は何かを誤解していますか、これはBashの標準入力を読み取るコマンドの一般的なルールとして使用できますか?


4
私は確かにあなたに「役に立たないseq」賞を許可します;
クリスダウン

2
あなたはどちらか比較すべきであることを注意cat <<ENDcat <<< "test string"echo "test string" | catまたはcat <<'END'cat <<< 'test string'対をecho 'test string' | cat文字列にシェルによって実行される展開の同じ量を持っています。
マナトワーク

@ChrisDown Derp。私はいつもそれを忘れます。ありがとう!
l0b0

@manatworkありがとう、修正および更新されたタイミング。
l0b0

ただの好奇心、その担当者とは何ですか?あなたの元々の意図は$ reps回ループしていませんでした$(seq $reps)か?
マナトワーク

回答:


11

まず、パフォーマンスに集中しましょう。Debian squeezeを実行している、それ以外はほとんどアイドル状態のx86_64プロセッサーで、わずかに異なるプログラムのベンチマークを実行しました。

herestring.bash、ヒアストリングを使用して入力行を渡す:

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

heredoc.bash、ヒアドキュメントを使用して入力行を渡す:

#! /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.bashechoおよびを使用して入力行を渡す:

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

比較のために、ATT ksh93およびダッシュの下でスクリプトの時間herestring.bashを計りました(ダッシュにはヒエストリングがないため、を除く)。

中央値の3回は次のとおりです。

$ 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

結論:

  • ヒアドキュメントはヒアストリングよりも高速です。
  • echoそして、パイプは顕著ですが、劇的に高速ではありません。(これはおもちゃのプログラムであることに注意してください:実際のプログラムでは、ほとんどの処理時間はtr呼び出しがここで何を意味するかになります。)
  • 速度が必要な場合は、bashを捨てて、代わりにdashまたはさらに良いkshを呼び出します。Bashの機能は、比較的遅いことを補いませんが、kshには機能と速度の両方があります。

パフォーマンスに加えて、明確さと移植性もあります。<<<はksh93 / bash / zsh拡張機能であり、echo … |or よりもあまり知られていません<<。ksh88 / pdkshまたはPOSIX shでは機能しません。

<<<ほぼ間違いなく明確な唯一の場所は、ヒアドキュメント内です。

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

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

(ほとんどのシェルは、を含む行の終わりで括弧を閉じることに対処できません<<EOF。)


2
<<<はから来てrcおり、によって最初にボーンのようなシェルの世界に持ち込まれたことに注意してくださいzshrc末尾の改行を追加しませんでしたが、すべてbashzshおよびksh93AFAICT を実行します。rcながら、そこにパイプを使用しbashzshそしてksh93ヒアドキュメントのためのような一時ファイルを使用します。
ステファンシャゼル

@StephaneChazelasおっと、チェックしておくべきだった、ありがとう。これは<<<さらに有用性を低下させます。
ジル「SO-悪であるのをやめる」

またzsh=(<<<foo)「foo」を含む一時ファイルを効果的に作成するための最適化があることに注意してください=(echo foo)
ステファンシャゼル

また、pdkshには含まれていないことにも触れます<<<
kurtm

@kurtm「ksh88 / pdkshまたはPOSIX shでは動作しません。」
ジル「SO-悪であるのをやめる」

6

ヒアドキュメントを使用するもう1つの理由は(十分な情報がない場合)、ストリームが消費されないとエコーが失敗する可能性があることです。bash ' pipefailオプションの使用を検討してください。

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

/bin/true標準入力を消費しませんが、それでもecho yawn完了します。ただし、エコーが大量のデータを印刷するように要求された場合、完了するまで完了しませんtrue

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)(bashはbash(1)に従って追加するため、128が追加されます。

致命的なシグナルNでコマンドが終了すると、bashは終了ステータスとして128 + Nの値を使用します。

Heredocsにはこの問題はありません。

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

おかげで、この特定のニュアンスにより、スクリプトがランダムな場所で断続的に失敗しました。kvmホストよりもAmazonサーバーのほうが断続的に失敗しましたが、失敗するように見える場所でも失敗することがありました。pipefailデフォルトではオフになっているのは良いことです!
mogsie

2
ああ、pipefailすべてのスクリプトの上部で有効にします。すべてのエラーをギミ!
l0b0

@ l0b0うーん。多分私はj.mp/safebashにpipefailを追加します開発中にすべてのエラーを取得するために私はすべてです!
ブルーノブロノスキー14年

2

エコーを使用する理由の1つは、heredocsおよびherestringsの最後に追加される改行文字を制御することです。

3文字のfoo長さは3です。

$ echo -n foo | wc -c
3

ただし、3文字のヒアストリングは4文字です。

$ wc -c <<< foo
4

3文字のヒアドキュメントも:

$ wc -c << EOF
foo
EOF
4

4番目の文字は改行0x0a文字です。

どういうわけか、これはサブシェルから出力を取得するときにbashがこれらの改行文字を削除する方法と魔法のように適合します。

ここでは4つの文字を返すコマンドは、次のとおりですfoo\n。「\ n」はエコーによって追加されます。-nオプションを指定しない限り、常に改行文字が追加されます。

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

ただし、これを変数に割り当てると、エコーによって追加された末尾の改行が削除されます。

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

したがって、ファイルと変数を組み合わせて計算に使用する場合(たとえば、heredocまたはherestringsは使用できません。改行が追加されるためです。

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

ifステートメントを読み取りに変更した場合

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

その後、テストに合格します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.