इको बनाम <<<, या बैश अवार्ड में इको का बेकार उपयोग?


19

अब तक यूज़लेस यूज़ ऑफ catअवार्ड बहुत अच्छी तरह से जाना जाता है, और इसमें यूज़लेस यूज़ ऑफ़echo (इस प्रश्न के लिए प्रासंगिक नहीं) का भी उल्लेख है । मैं सोच रहा था कि क्या " echoबैश अवार्ड में बेकार उपयोग" होना चाहिए : पाइपिंग कुछ अत्यधिक अवैज्ञानिक मापों के अनुसार हेरेडोक्स और हेस्ट्रेस्ट की तुलना में बहुत धीमी लगती है:

  • 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
  • पुनर्निर्देशन

    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

सामान्य तौर पर, हेरेडोक्स और हेर्स्ट्रेस एक ही गति के बारे में होते हैं (यह कई परीक्षणों से सिर्फ एक डेटा सेट है) जबकि पुनर्निर्देशन लगातार 50% से अधिक धीमा है। क्या मुझे कुछ गलत समझ में आ रहा है, या इसे बैश में मानक इनपुट पढ़ने वाले कमांड के लिए एक सामान्य नियम के रूप में इस्तेमाल किया जा सकता है?


4
मैं निश्चित रूप से आप एक "की बेकार उपयोग की अनुमति देंगे seqपुरस्कार ;-)"
क्रिस नीचे

2
ध्यान दें कि आप या तो की तुलना करनी चाहिए cat <<ENDबनाम cat <<< "test string"बनाम echo "test string" | catया cat <<'END'बनाम cat <<< 'test string'बनाम echo 'test string' | catएक ही राशि के विस्तार तार पर खोल द्वारा किया जाता है।
मैनटवर्क

@ क्रिसडाउन डेरप। मैं हमेशा उस एक को भूल जाता हूं। धन्यवाद!
l0b0

@manatwork धन्यवाद, निश्चित और अद्यतन समय।
l0b0

बस एक जिज्ञासा है, उस प्रतिनिधि के साथ क्या है? क्या आपका मूल इरादा $ reps बार को लूप करने का नहीं था $(seq $reps)?
मैनटवर्क

जवाबों:


11

सबसे पहले, प्रदर्शन पर ध्यान केंद्रित करते हैं। मैं एक अलग ज्यादातर निष्क्रिय 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

तुलना के लिए, मैंने एटीटी ksh93 के तहत और डैश के तहत स्क्रिप्ट्स को भी समय पर छोड़ दिया (इसके अलावा herestring.bash, क्योंकि डैश में हेरस्ट्रेस नहीं है)।

यहाँ तीन बार के मध्यकाल हैं:

$ 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कॉल स्टैंड के लिए होता है।)
  • यदि आप गति चाहते हैं, तो बैश और कॉल डैश या इसके बजाय बेहतर ksh। बैश की विशेषताएं इसके सापेक्ष धीमापन के लिए नहीं बनती हैं, लेकिन ksh में विशेषताएं और गति दोनों हैं।

प्रदर्शन से परे, स्पष्टता और सुवाह्यता भी है। <<<ksh93 / bash / zsh एक्सटेंशन है, जो echo … |या की तुलना में कम प्रसिद्ध है <<। यह 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पीछे न्यू लाइन जोड़ने नहीं किया, लेकिन के सभी bash, zshऔर ksh93AFAICT है। rcपाइप का उपयोग करता है, जबकि bash, zshऔर ksh93heredocs के लिए एक अस्थायी फ़ाइल का उपयोग करें।
स्टीफन चेज़लस

@ स्टेफेनचैलेजस उफ़, मुझे जाँच करनी चाहिए थी, धन्यवाद। यह <<<भी कम उपयोगी बनाता है ।
गिल्स एसओ- बुराई को रोकना '

यह भी ध्यान दें कि प्रभावी रूप से एक अस्थायी फ़ाइल बनाने के लिए zshएक अनुकूलन है =(<<<foo)जिसमें "फू" शामिल है जिसमें एक प्रक्रिया को शामिल करने के लिए फोर्किंग शामिल नहीं है =(echo foo)
स्टीफन चेज़लस

मैं यह भी उल्लेख करूँगा कि pdksh शामिल नहीं है<<<
कर्टम

@ कुर्टम "यह ksh88 / pdksh या POSIX श में काम नहीं करता है।"
गाइल्स का SO- बुराई होना बंद हो '

6

हेरेडोस्क का उपयोग करने का एक और कारण (यदि आपके पास पर्याप्त नहीं था) यह है कि यदि धारा का सेवन नहीं किया जाता है तो गूंज विफल हो सकती है। बैश 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) (128 जोड़ा जा रहा है क्योंकि बैश बैश के अनुसार ऐसा करता है (1):

जब कोई कमांड घातक सिग्नल एन पर समाप्त हो जाती है, तो बैश बाहर निकलने की स्थिति के रूप में 128 + एन के मूल्य का उपयोग करता है।

Heredocs में यह समस्या नहीं है:

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

धन्यवाद, यह विशेष बारीकियों के कारण मेरी स्क्रिप्ट यादृच्छिक स्थानों पर रुक-रुक कर विफल हो रही थी, kvm मेजबानों की तुलना में अमेज़ॅन सर्वरों पर अधिक, लेकिन यह अभी भी समय-समय पर असंभव-से-असफल स्थानों में विफल रहा। यह अच्छी बात है कि pipefailबंद होने की चूक!
मोगसी

2
ओह, मैं pipefailहर स्क्रिप्ट के शीर्ष पर सक्षम हूं । सभी त्रुटियों को दूर करो!
l0b0

@ l0b0 हम्म। हो सकता है कि मैं j.mp/safebash में pipefail जोड़ूं, मैं विकास के दौरान सभी त्रुटियों को प्राप्त करने के लिए हूं!
ब्रूनो ब्रोंस्की

2

एक कारण जो आप इको का उपयोग करना चाहते हैं, वह कुछ नियंत्रण को रेखांकित करने के लिए है, जो कि वंशावली और हिरन के झुंड में जोड़े जाने वाले न्यूलाइन वर्ण को नियंत्रित करता है:

तीन वर्णों fooकी लंबाई 3 है:

$ echo -n foo | wc -c
3

हालांकि, एक थ्रेचर ब्रेकरिंग चार वर्ण हैं:

$ wc -c <<< foo
4

एक तीन-वर्ण का वंशानुगत भी:

$ wc -c << EOF
foo
EOF
4

चौथा चरित्र एक नया 0x0aचरित्र है।

किसी तरह यह जादुई तरीके से फिट बैठता है जिस तरह से उप-शेल से आउटपुट हथियाने पर बैश इन न्यूलाइन वर्णों को हटा देता है:

यहाँ एक कमांड है जो चार वर्णों को लौटाता है: fooऔर \n। '\ N' प्रतिध्वनि द्वारा जोड़ा जाता है, यह हमेशा एक नया वर्ण जोड़ता है जब तक आप -nविकल्प निर्दिष्ट नहीं करते हैं :

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

हालाँकि, इसे किसी वैरिएबल में असाइन करने से, इको द्वारा जोड़ी गई अनुगामी न्यूलाइन को हटा दिया जाता है:

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

इसलिए यदि आप फ़ाइलें और चर मिलाते हैं और उनका उपयोग गणनाओं में करते हैं (उदाहरण के लिए, आप वंशानुगत या हेरस्ट्रेस का उपयोग नहीं कर सकते, क्योंकि वे एक नई पंक्ति जोड़ देंगे।

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 [ $(echo -n "$foo" | wc -c) -eq $(wc -c < something.txt) ] ; then

फिर परीक्षा पास होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.