_gem_dec() { shift $# ; . /dev/fd/3
} 3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
for func in guard rspec rake ; do _gem_dec $func ; done
echo "_guard ; _rspec ; _rake are all functions now."
उपरोक्त . source /dev/fd/3जिसे _gem_dec()हर बार फ़ंक्शन में खिलाया जाता है, इसे पूर्व-मूल्यांकन के रूप में कहा जाता है here-document. _gem_dec'sकेवल नौकरी एक पैरामीटर प्राप्त करना है और इसे bundle execलक्ष्य के रूप में और फ़ंक्शन के नाम के रूप में पूर्व-मूल्यांकन करना है जिसमें इसे लक्षित किया गया है।
NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
हालांकि उपरोक्त मामले में, मुझे नहीं लगता कि कोई जोखिम हो सकता है।
यदि उपरोक्त कोड-ब्लॉक को एक .bashrcफ़ाइल में कॉपी किया जाता है , तो न केवल शेल फ़ंक्शन होंगे _guard(), _rspec()और_rake() लॉगिन पर घोषित किए जाएंगे, बल्कि _gem_dec()फ़ंक्शन आपके शेल प्रॉम्प्ट (या अन्यथा) पर किसी भी समय निष्पादन के लिए उपलब्ध होगा और इसलिए नए अस्थायी कार्य कर सकते हैं जब भी आपको पसंद हो, केवल घोषित किया जाए:
_gem_dec $new_templated_function_name
और मुझे दिखाने के लिए @Andrew के लिए धन्यवाद, ए द्वारा खाया नहीं जाएगा for loop.
पर कैसे?
मैं आदत के बाहर खुला 3रखने के लिए ऊपर दिए गए फ़ाइल डिस्क्रिप्टर का उपयोग करता हूं stdin, stdout, and stderr, or <&0 >&1 >&2- हालांकि, जैसा कि मैं यहां लागू कुछ अन्य डिफ़ॉल्ट सावधानियों के लिए भी कर रहा हूं - क्योंकि परिणामी फ़ंक्शन इतना सरल है, यह वास्तव में आवश्यक नहीं है। यह अच्छा अभ्यास है, हालांकि। कॉलिंग shift $#उन अनावश्यक सावधानियों में से एक है।
फिर भी, जब एक फ़ाइल के रूप में निर्दिष्ट किया जाता है <inputया>output के साथ [optional num]<fileया [optional num]>fileपुनर्निर्देशन गिरी एक फ़ाइल वर्णनकर्ता, के माध्यम से पहुँचा जा सकता है जो में पढ़ता है character deviceमें विशेष फ़ाइलों /dev/fd/[0-9]*। यदि [optional num]विनिर्देशक को छोड़ दिया जाता है, तो 0<fileइनपुट के लिए और 1>fileआउटपुट के लिए मान लिया जाता है । इस पर विचार करो:
l='line %d\n' ; printf "$l" 1 2 3 4 5 6 >/dev/fd/1
> line 1
> line 2
> line 3
> line 4
> line 5
> line 6
( printf "$l" 4 5 6 >/dev/fd/3 ; printf "$l" 1 2 3 ) >/tmp/sample 3>/tmp/sample2
( cat /tmp/sample2 ) </tmp/sample
> line 4
> line 5
> line 6
( cat /dev/fd/0 ) </tmp/sample
> line 1
> line 2
> line 3
( cat /dev/fd/3 ) </tmp/sample 3</tmp/sample2
> line 4
> line 5
> line 6
और क्योंकि here-documentहम कोड-ब्लॉक में फ़ाइल इनलाइन का वर्णन करने का केवल एक साधन है, जब हम करते हैं:
<<'HEREDOC'
[$CODE]
HEREDOC
हम भी कर सकते हैं:
echo '[$CODE]' >/dev/fd/0
एक बहुत ही महत्वपूर्ण अंतर के साथ। ऐसा न करने पर एक की तो खोल खोल के लिए इसका मूल्यांकन करेंगे की तरह:"'\quote'"<<"'\LIMITER"'here-document$expansion
echo "[$CODE]" >/dev/fd/0
तो, के लिए _gem_dec(),3<<-FUNC here-document इनपुट पर एक फ़ाइल, एक ही रूप में यह अगर यह थे होगा के रूप में मूल्यांकन किया जाता है 3<~/some.file को छोड़कर कि क्योंकि हम छोड़ FUNCसीमक उद्धरण के नि: शुल्क है, यह पहले के लिए मूल्यांकन किया जाता है $expansion.इस बारे में महत्वपूर्ण बात यह है कि यह इनपुट, जिसका अर्थ है यह केवल के लिए मौजूद है _gem_dec(),लेकिन वह यह भी पहले मूल्यांकन किया जाता है _gem_dec()समारोह रन हमारे खोल पढ़ सकते हैं और इसके मूल्यांकन करने के लिए नहीं है, क्योंकि $expansionsइससे पहले कि इनपुट के रूप में सौंपने इसे बंद।
करने देता है guard,, उदाहरण के लिए:
_gem_dec guard
तो पहले शेल को इनपुट को हैंडल करना होगा, जिसका अर्थ है पढ़ना:
3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
फ़ाइल डिस्क्रिप्टर 3 में और शेल विस्तार के लिए इसका मूल्यांकन करना। अगर इस समय आप भागे:
cat /dev/fd/3
या:
cat <&3
चूंकि वे दोनों समान आदेश हैं जिन्हें आप देखेंगे:
_guard() { [ ! -e 'Gemfile' ] && {
command guard "$@" ; return $?
} || bundle exec guard "$@"
}
... समारोह में किसी भी कोड से पहले कभी भी निष्पादित करता है। यह सब के बाद <input, फ़ंक्शन है । अधिक उदाहरणों के लिए यहां एक अलग प्रश्न के लिए मेरा उत्तर देखें ।
(* तकनीकी तौर पर यह पूरी तरह सच नहीं है। क्योंकि मैं एक प्रमुख का उपयोग -dashकरने से पहले here-doc limiter, सब से ऊपर बाएं से उचित किया जाएगा। लेकिन मैं प्रयोग किया जाता है -dashतो मैं कर सकता है <tab-insert>पहली जगह में पठनीयता के लिए तो मैं नहीं पट्टी करने जा रहा हूँ <tab-inserts>से पहले आपको इसे पढ़ने की पेशकश ...)
इस बारे में सबसे अच्छा हिस्सा उद्धरण है - ध्यान दें कि '"उद्धरण बने हुए हैं और केवल \उद्धरण छीन लिए गए हैं। यह संभवतः इस कारण से किसी भी अन्य से अधिक है कि यदि आपको एक शेल का दो बार मूल्यांकन करना है तो $expansionमैं सिफारिश करूंगा here-documentक्योंकि उद्धरणों की तुलना में बहुत आसान है eval।
वैसे भी, अब उपर्युक्त कोड ठीक उसी तरह से फाइल है जैसे 3<~/heredoc.fileकि _gem_dec()फंक्शन आने के इंतजार में और उसके इनपुट को स्वीकार करने के लिए /dev/fd/3।
इसलिए जब हम _gem_dec()पहला काम शुरू करते हैं तो मैं सभी स्थितिगत मापदंडों को टॉस करता हूं , क्योंकि हमारा अगला कदम दो बार मूल्यांकन किया गया शेल विस्तार है और मैं नहीं चाहता कि निहित में $expansionsसे कोई भी मेरे वर्तमान $1 $2 $3...मापदंडों के रूप में व्याख्या की जाए । तो मैं:
shift $#
shiftकई के रूप में छोड देता है positional parametersआपके द्वारा निर्दिष्ट और से शुरू होता है के रूप में $1क्या रहता है के साथ। तो अगर मैं कहा जाता _gem_dec one two threeप्रॉम्प्ट पर _gem_dec's $1 $2 $3स्थितीय मापदंडों होगा one two threeऔर कुल वर्तमान स्थितीय गिनती, या $#3. होगा मैं तो कहा जाता है, तो shift 2,के मूल्यों oneऔरtwo की जाएगी shiftएड का मूल्य दूर, $1करने के लिए बदल जाएगा threeऔर $#करने के लिए विस्तार होगा 1. तो shift $#बस उन सबको दूर फेंक देता है। ऐसा करना पूरी तरह से एहतियाती है और कुछ समय के लिए इस तरह का काम करने के बाद विकसित हुई एक आदत है। यहाँ यह (subshell)स्पष्टता के लिए थोड़ा फैला हुआ है:
( set -- one two three ; echo "$1 $2 $3" ; echo $# )
> one two three
> 3
( set -- one two three ; shift 2 ; echo "$1 $2 $3" ; echo $# )
> three
> 1
( set -- one two three ; shift $# ; echo "$1 $2 $3" ; echo $# )
>
> 0
वैसे भी, अगला कदम वह होता है जहाँ जादू होता है। यदि आप . ~/some.shशेल प्रॉम्प्ट पर हैं, तो घोषित किए गए सभी फ़ंक्शन और पर्यावरण चर ~/some.shआपके शेल प्रॉम्प्ट पर कॉल करने योग्य होंगे। यही बात लागू होती यहाँ, है हम को छोड़कर हमारे फ़ाइल वर्णनकर्ता के लिए विशेष फ़ाइल, या - जो है, जहां हमारे इन-लाइन फ़ाइल pathed किया गया है - और हम अपने समारोह की घोषणा की है। और इसी तरह यह काम करता है।. sourcecharacter device. /dev/fd/3here-document
_guard
अब आपके _guardफंक्शन को जो भी करना चाहिए, करता है।
परिशिष्ट:
अपनी स्थिति को बचाने के लिए कहने का एक शानदार तरीका:
f() { . /dev/fd/3
} 3<<-ARGS
args='${args:-"$@"}'
ARGS
संपादित करें:
जब मैंने पहली बार इस प्रश्न का उत्तर दिया तो मैंने एक शेल घोषित करने की समस्या पर अधिक ध्यान केंद्रित किया जो function()अन्य कार्यों को घोषित करने में सक्षम था जो वर्तमान शेल $ENVइस्त्री में जारी रहेगा, जैसा कि मैंने पूछा कि निरंतर कार्यों के साथ क्या करेंगे। तब से मैंने महसूस किया है कि मेरे मूल रूप से हल किए गए समाधान 3<<-FUNCने रूप लिया:
3<<-FUNC
_${1}() {
if [ -e 'Gemfile' ]; then
bundle exec $1 "\$@"
else
command _${1} "\$@"
}
FUNC
संभावना के रूप में प्रश्नकर्ता के लिए उम्मीद है क्योंकि मैं विशेष रूप से से कथात्मक समारोह का नाम बदल दिया काम किया है नहीं होगा $1करने के लिए _${1}है, जो अगर की तरह कहा जाता है _gem_dec guardउदाहरण के लिए, में परिणाम होगा _gem_decनाम के एक समारोह की घोषणा _guardके रूप में बस के लिए विरोध किया guard।
नोट: इस तरह का व्यवहार मेरे लिए आदत की बात है - मैं आमतौर पर अनुमान पर काम करता हूं कि शेल फ़ंक्शन केवल अपने स्वयं केकब्जे में होने चाहिए_namespaceताकिnamespaceशेलपर उनके घुसपैठ सेcommandsउचित रूपसे बचें।
यह एक सार्वभौमिक आदत नहीं है, हालांकि, जैसा कि commandकॉल करने के लिए पूछने वाले के उपयोग में विकसित किया गया है $1।
आगे की परीक्षा मुझे निम्नलिखित पर विश्वास करने के लिए प्रेरित करती है:
पूछने वाला चाहता है कि शेल फ़ंक्शन नाम guard, rspec, or rake, जिसे कहा जाता है, rubyउसी नाम ifके एक फंक्शन को संकलित करेगा जिस फ़ाइल में Gemfileमौजूद है $PATH या
if Gemfile मौजूद नहीं है, शेल फ़ंक्शन को rubyउसी नाम के फ़ंक्शन को निष्पादित करना चाहिए ।
यह पहले काम नहीं करता था क्योंकि मैंने पढ़ने के लिए $1कॉल को बदल दिया था command:
command _${1}
जिसके परिणामस्वरूप rubyशेल फ़ंक्शन संकलित नहीं होता है:
bundle exec $1
मुझे आशा है कि आप देख सकते हैं (जैसा कि अंततः मैंने किया था) ऐसा लगता है कि पूछने वाला केवल commandअप्रत्यक्ष रूप से निर्दिष्ट करने के लिए उपयोग कर रहा है namespaceक्योंकि एक ही नाम के शेल फ़ंक्शन पर एक commandनिष्पादन योग्य फ़ाइल को कॉल करना पसंद करेंगे $PATH।
यदि मेरा विश्लेषण सही है (जैसा कि मुझे उम्मीद है कि पुष्टिकरण की पुष्टि होगी) तो यह:
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
उन शर्तों को बेहतर तरीके से संतुष्ट करना चाहिए, जो guardप्रॉम्प्ट पर कॉल कर रहे हैं , केवल$PATH नाम में एक निष्पादन योग्य फ़ाइल निष्पादित करने का प्रयास करेंगे, guardजबकि _guardप्रॉम्प्ट पर कॉलिंग Gemfile'sअस्तित्व की जांच करेगी और तदनुसार निष्पादित या निष्पादन guardयोग्य में निष्पादित करेगी $PATH। इस तरह namespaceसे संरक्षित है और, जैसा कि मुझे लगता है कि कम से कम, यह पूछने वाले का इरादा अभी भी पूरा हुआ है।
वास्तव में, हमारे खोल समारोह मानते हुए _${1}()और निष्पादन योग्य ${PATH}/${1}हैं केवल दो तरीके हमारे खोल के लिए एक कॉल व्याख्या की जा सकती है या तो $1या _${1}तो के उपयोग commandबिल्कुल भी समारोह में अब पूरी तरह से बेमानी बना दिया है। फिर भी, मैंने इसे वैसे ही रहने दिया जैसे कि मैं एक ही गलती से दो बार करना पसंद नहीं करता ... वैसे भी एक पंक्ति में।
यदि यह पूछने वाले के लिए अस्वीकार्य है और वह / वह _पूरी तरह से दूर करना पसंद करेंगे , तो अपने वर्तमान रूप में, _underscoreसभी संपादनकर्ता को अपनी आवश्यकताओं को पूरा करने के लिए सभी की आवश्यकता होनी चाहिए जैसा कि मैं उन्हें समझता हूं।
उस परिवर्तन के अलावा, मैंने मूल सिंटैक्स के बजाय शॉर्ट-सर्किट सशर्त का उपयोग करने &&और / या|| शेल करने के लिए फ़ंक्शन को भी संपादित किया है । इस तरह से कथन का मूल्यांकन केवल तभी किया जाता है जब वह अंदर न हो । हालाँकि, यह सुनिश्चित करने के लिए कि इस घटना के न होने की स्थिति में नहीं चलने के अलावा इस संशोधन की आवश्यकता है, लेकिन फ़ंक्शन 0 के अलावा कुछ भी नहीं देता है ।if/thencommandGemfile$PATHreturn $?bundleGemfileruby $1
अंतिम, मुझे ध्यान देना चाहिए कि यह समाधान केवल पोर्टेबल शेल निर्माण को लागू करता है। दूसरे शब्दों में, यह POSIX संगतता का दावा करने वाले किसी भी शेल में समान परिणाम प्रस्तुत करना चाहिए। हालांकि, यह निश्चित रूप से, मेरे लिए यह दावा करने के लिए बकवास होगा कि प्रत्येक POSIX- संगत प्रणाली को ruby bundleनिर्देश को संभालना चाहिए , कम से कम शेल कॉलर्स को इस पर व्यवहार करना चाहिए चाहे कॉल शेल shया हो dash। इसके अलावा उपरोक्त दोनों ( और वैसे भी कम से कम आधे रास्ते-लेन कीshopts उम्मीद ) के रूप में काम करेगा ।bashzsh
for loop?मेरे मतलब से नहीं खाया जाता है,for loopआमतौर पर गायब घोषित किए गए चर - मैं समान कारणों के लिए समान कार्यों की अपेक्षा करता हूं।