हां, हम कई चीजों को देखते हैं जैसे:
while read line; do
echo $line | cut -c3
done
या खराब:
for line in `cat file`; do
foo=`echo $line | awk '{print $2}'`
echo whatever $foo
done
(हँसो मत, मैंने उनमें से कई को देखा है)।
आम तौर पर शेल स्क्रिप्टिंग शुरुआती से। सी या पाइथन जैसी अनिवार्य भाषाओं में आप जो करते हैं, उसके वे भोले शाब्दिक अनुवाद हैं, लेकिन ऐसा नहीं है कि आप कैसे गोले में काम करते हैं, और वे उदाहरण बहुत ही अक्षम हैं, पूरी तरह से अविश्वसनीय हैं (संभावित रूप से सुरक्षा के मुद्दों के लिए अग्रणी), और यदि आप कभी भी प्रबंधन करते हैं अधिकांश बग को ठीक करने के लिए, आपका कोड अवैध हो जाता है।
सैद्धांतिक रूप
सी या अधिकांश अन्य भाषाओं में, बिल्डिंग ब्लॉक कंप्यूटर निर्देशों से सिर्फ एक स्तर ऊपर हैं। आप अपने प्रोसेसर को बताएं कि आगे क्या करना है और फिर क्या करना है। आप अपने प्रोसेसर को हाथ से लेते हैं और इसे माइक्रो-मैनेज करते हैं: आप उस फ़ाइल को खोलते हैं, आप पढ़ते हैं कि कई बाइट्स, आप ऐसा करते हैं, आप इसके साथ ऐसा करते हैं।
गोले एक उच्च स्तरीय भाषा है। कोई कह सकता है कि यह भाषा भी नहीं है। वे सभी कमांड लाइन दुभाषियों से पहले हैं। काम उन कमांडों द्वारा किया जाता है जिन्हें आप चलाते हैं और शेल केवल उन्हें ऑर्केस्ट्रेट करने के लिए है।
यूनिक्स द्वारा पेश की गई महान चीजों में से एक पाइप था और उन डिफ़ॉल्ट स्टड / stdout / stderr धाराएं जो सभी कमांड डिफ़ॉल्ट रूप से संभालती हैं।
45 वर्षों में, हमने आदेशों की शक्ति का उपयोग करने के लिए एपीआई से बेहतर नहीं पाया है और उन्हें एक कार्य में सहयोग किया है। शायद यही मुख्य कारण है कि लोग आज भी गोले का उपयोग कर रहे हैं।
आपको एक कटिंग टूल और एक ट्रांसपेरेंट टूल मिला है, और आप बस कर सकते हैं:
cut -c4-5 < in | tr a b > out
शेल केवल प्लंबिंग कर रहा है (फाइलों को खोलें, पाइपों को सेटअप करें, कमांड्स को इनवाइट करें) और जब यह सब तैयार हो जाता है, तो यह बिना कुछ किए शेल को प्रवाहित करता है। उपकरण अपना काम समवर्ती रूप से करते हैं, कुशलता से अपनी गति से पर्याप्त बफ़रिंग के साथ ताकि एक दूसरे को अवरुद्ध न करें, यह सिर्फ सुंदर और अभी तक सरल है।
एक उपकरण को शामिल करना हालांकि एक लागत है (और हम प्रदर्शन बिंदु पर इसे विकसित करेंगे)। उन उपकरणों को सी में हजारों निर्देशों के साथ लिखा जा सकता है। एक प्रक्रिया बनानी होगी, उपकरण को लोड करना होगा, आरंभ करना होगा, फिर साफ किया जाएगा, प्रक्रिया नष्ट हो जाएगी और इंतजार करना होगा।
आमंत्रित करना cutरसोई के दराज को खोलने के समान है, चाकू ले लो, इसका उपयोग करें, इसे धो लें, इसे सूखा दें, इसे वापस दराज में डालें। जब तुम करोगे:
while read line; do
echo $line | cut -c3
done < file
यह फ़ाइल की प्रत्येक पंक्ति के लिए है, readरसोई दराज से उपकरण प्राप्त करना (एक बहुत ही भद्दा क्योंकि यह उसके लिए डिज़ाइन नहीं किया गया है ), एक पंक्ति पढ़ें, अपना पढ़ा उपकरण धोएं, इसे वापस दराज में रखें। फिर echoऔर cutउपकरण के लिए एक बैठक निर्धारित करें, उन्हें दराज से प्राप्त करें, उन्हें आह्वान करें, उन्हें धोएं, उन्हें सूखा दें, उन्हें वापस दराज में रखें और इसी तरह।
उन उपकरणों में से कुछ ( readऔर echo) अधिकांश गोले में बनाए गए हैं, लेकिन यह मुश्किल से यहाँ फर्क पड़ता है echoऔर cutअभी भी अलग-अलग प्रक्रियाओं में चलाने की आवश्यकता है।
यह एक प्याज को काटने जैसा है लेकिन अपने चाकू को धोने और इसे प्रत्येक स्लाइस के बीच किचन दराज में रख दें।
यहां स्पष्ट तरीका यह है कि आप अपने cutउपकरण को दराज से प्राप्त करें , अपने पूरे प्याज को स्लाइस करें और पूरी नौकरी करने के बाद इसे वापस दराज में रख दें।
IOW, गोले में, विशेष रूप से पाठ को संसाधित करने के लिए, आप संभव के रूप में कुछ उपयोगिताओं को आमंत्रित करते हैं और उन्हें कार्य में सहयोग करते हैं, क्रम में हजारों उपकरण नहीं चलाते हैं ताकि अगले एक को चलाने से पहले शुरू करने, चलाने, साफ करने के लिए प्रतीक्षा करें।
आगे ब्रूस के ठीक जवाब में पढ़ना । गोले में निम्न-स्तरीय पाठ प्रसंस्करण आंतरिक उपकरण (शायद को छोड़कर zsh) सीमित, बोझिल हैं, और आम तौर पर सामान्य पाठ प्रसंस्करण के लिए फिट नहीं होते हैं।
प्रदर्शन
जैसा कि पहले कहा गया था, एक कमांड को चलाने में एक लागत है। एक बड़ी लागत अगर वह कमांड नहीं है, लेकिन भले ही वे बिलिन हैं, तो लागत बड़ी है।
और गोले को इस तरह चलाने के लिए डिज़ाइन नहीं किया गया है, उन्हें प्रदर्शनशील प्रोग्रामिंग भाषा होने का कोई दिखावा नहीं है। वे नहीं हैं, वे सिर्फ कमांड लाइन दुभाषिए हैं। इसलिए, इस मोर्चे पर थोड़ा अनुकूलन किया गया है।
इसके अलावा, गोले अलग-अलग प्रक्रियाओं में कमांड चलाते हैं। उन बिल्डिंग ब्लॉक्स में एक आम मेमोरी या स्टेट शेयर नहीं होता है। जब आप C fgets()या fputs()C में करते हैं, तो stdio में एक फंक्शन होता है। stdio इनपुट और आउटपुट के लिए सभी stdio फ़ंक्शंस के लिए आंतरिक बफ़र्स रखता है, महंगा सिस्टम कॉल करने से बचने के लिए अक्सर।
इसी भी builtin खोल उपयोगिताओं ( read, echo, printf) ऐसा नहीं कर सकते। readएक पंक्ति को पढ़ने के लिए है। यदि यह न्यूलाइन कैरेक्टर को पढ़ता है, तो इसका मतलब है कि आपके द्वारा चलाया जाने वाला अगला कमांड इसे याद करेगा। तो readएक समय में इनपुट एक बाइट को पढ़ना है (कुछ कार्यान्वयन में एक अनुकूलन है यदि इनपुट एक नियमित फ़ाइल है जिसमें वे विखंडू पढ़ते हैं और वापस चाहते हैं, लेकिन यह केवल नियमित फ़ाइलों के लिए काम करता है और bashउदाहरण के लिए केवल 128 बाइट विखंडू पढ़ता है जो कि है अभी भी पाठ उपयोगिताओं की तुलना में बहुत कम है)।
आउटपुट पक्ष पर समान, echoइसके आउटपुट को केवल बफर नहीं कर सकता, इसे सीधे आउटपुट करना होगा क्योंकि आपके द्वारा चलाया जाने वाला अगला कमांड उस बफर को साझा नहीं करेगा।
जाहिर है, क्रमिक रूप से चलने का मतलब है कि आपको उनके लिए इंतजार करना होगा, यह थोड़ा शेड्यूलर नृत्य है जो शेल से और टूल और वापस से नियंत्रण देता है। इसका मतलब यह भी है (एक पाइपलाइन में उपकरणों के लंबे समय तक चलने के उपयोग के विपरीत) जो उपलब्ध होने पर आप एक ही समय में कई प्रोसेसर का उपयोग नहीं कर सकते।
उस while readलूप और (माना जाता है) के बराबर cut -c3 < file, मेरे त्वरित परीक्षण में, मेरे परीक्षणों में लगभग 40000 का सीपीयू समय अनुपात है (एक सेकंड बनाम आधा दिन)। लेकिन भले ही आप केवल शेल बिल्डरों का उपयोग करें:
while read line; do
echo ${line:2:1}
done
(यहां bash), वह अभी भी लगभग 1: 600 (एक सेकंड बनाम 10 मिनट) है।
विश्वसनीयता / स्पष्टता
उस कोड को प्राप्त करना बहुत कठिन है। मैंने जो उदाहरण दिए वे बहुत बार जंगली में देखे गए हैं, लेकिन उनके पास कई कीड़े हैं।
readएक आसान उपकरण है जो कई अलग-अलग काम कर सकता है। यह उपयोगकर्ता से इनपुट पढ़ सकता है, इसे विभिन्न चर में स्टोर करने के लिए शब्दों में विभाजित कर सकता है। read lineकरता नहीं इनपुट की एक पंक्ति पढ़ते हैं, या हो सकता है यह एक बहुत ही विशेष तरीके से एक लाइन पढ़ता है। यह वास्तव में इनपुट से उन शब्दों को पढ़ता है जिनके द्वारा अलग किए गए शब्द $IFSऔर जहां बैकस्लैश का उपयोग विभाजकों या न्यूलाइन वर्ण से बचने के लिए किया जा सकता है।
$IFSजैसे इनपुट पर डिफ़ॉल्ट मान के साथ:
foo\/bar \
baz
biz
read lineस्टोर करेगा "foo/bar baz"में $line, नहीं " foo\/bar \"के रूप में आप उम्मीद थी।
एक पंक्ति पढ़ने के लिए, आपको वास्तव में आवश्यकता है:
IFS= read -r line
यह बहुत सहज नहीं है, लेकिन यह जिस तरह से है, याद है कि गोले उस तरह से इस्तेमाल करने के लिए नहीं थे।
उसी के लिए echo। echoदृश्यों का विस्तार करता है। आप किसी यादृच्छिक फ़ाइल की सामग्री की तरह मनमानी सामग्री के लिए इसका उपयोग नहीं कर सकते। आपको printfइसके बजाय यहाँ की आवश्यकता है।
और निश्चित रूप से, आपके चर को उद्धृत करने की विशिष्ट भूल है जो हर कोई गिर जाता है। तो यह अधिक है:
while IFS= read -r line; do
printf '%s\n' "$line" | cut -c3
done < file
अब, कुछ और चेतावनी:
- इसके अलावा
zsh, यदि इनपुट में NUL वर्ण नहीं है तो काम नहीं करता है जबकि कम से कम GNU टेक्स्ट उपयोगिताओं में समस्या नहीं होगी।
- यदि अंतिम न्यूलाइन के बाद डेटा है, तो इसे छोड़ दिया जाएगा
- लूप के अंदर, स्टड को पुनर्निर्देशित किया जाता है, इसलिए आपको ध्यान देने की आवश्यकता है कि इसमें दिए गए आदेश स्टड से नहीं पढ़ते हैं।
- छोरों के भीतर आदेशों के लिए, हम ध्यान नहीं दे रहे हैं कि वे सफल होते हैं या नहीं। आमतौर पर, त्रुटि (डिस्क पूर्ण, त्रुटियों को पढ़ें ...) स्थितियों को खराब संभाला जाएगा, आमतौर पर सही समकक्ष के मुकाबले अधिक खराब ।
यदि हम उपरोक्त कुछ मुद्दों को संबोधित करना चाहते हैं, तो यह हो जाता है:
while IFS= read -r line <&3; do
{
printf '%s\n' "$line" | cut -c3 || exit
} 3<&-
done 3< file
if [ -n "$line" ]; then
printf '%s' "$line" | cut -c3 || exit
fi
वह कम और कम सुपाठ्य होता जा रहा है।
तर्कों के माध्यम से आदेश देने या चर में अपने उत्पादन को पुनः प्राप्त करने के लिए डेटा पारित करने के साथ कई अन्य मुद्दे हैं:
- तर्कों के आकार पर सीमा (कुछ पाठ उपयोगिता कार्यान्वयन के रूप में अच्छी तरह से वहाँ एक सीमा है, हालांकि उन तक पहुँचने के प्रभाव आम तौर पर कम समस्याग्रस्त हैं)
- एनयूएल चरित्र (पाठ उपयोगिताओं के साथ एक समस्या भी)।
- विकल्प के रूप में लिया गया तर्क जब वे
-(या +कभी-कभी) शुरू करते हैं
- आम तौर पर उन लूप्स में उपयोग किए जाने वाले विभिन्न कमांडों के विभिन्न quirks
expr, test...
- सीमित (सीमित) विभिन्न गोले के पाठ हेरफेर ऑपरेटर जो असंगत तरीकों से बहु-बाइट वर्णों को संभालते हैं।
- ...
सुरक्षा के विचार
जब आप शेल चर और कमांड के तर्क के साथ काम करना शुरू करते हैं, तो आप एक खदान क्षेत्र में प्रवेश कर रहे हैं।
यदि आप अपने चरों को उद्धृत करना भूल जाते हैं, विकल्प मार्कर के अंत को भूल जाते हैं , तो मल्टी-बाइट वर्ण (इन दिनों आदर्श) के साथ स्थानों में काम करते हैं, तो आप बग का परिचय देना निश्चित करते हैं जो जल्द या बाद में भेद्यता बन जाएगा।
जब आप लूप का उपयोग करना चाह सकते हैं।
TBD
yesलिखता है?