विस्मयादिबोधक चिह्न क्यों `` कभी-कभी परेशान बैश?


14

मुझे पता है कि !कमांडलाइन इतिहास के संदर्भ में कमांडलाइन पर विशेष महत्व है, लेकिन इससे अलग, एक रनिंग स्क्रिप्ट में विस्मयादिबोधक चिह्न कभी-कभी एक पार्सिंग त्रुटि का कारण बन सकता है।
मुझे लगता है कि यह एक के साथ कुछ करना है event, लेकिन मुझे नहीं पता कि एक घटना क्या है या यह क्या करता है। फिर भी, एक ही कमांड अलग-अलग स्थितियों में अलग-अलग व्यवहार कर सकता है।
अंतिम उदाहरण, नीचे, एक त्रुटि का कारण बनता है; लेकिन क्यों, जब एक ही कोड कमांड प्रतिस्थापन के बाहर काम करता है? .. GNU बैश का उपयोग 4.1.5

# This works, with or without a space between ! and p
  { echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }
# bar
# bar

# This works, works when there is a space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"
# bar

# This causes an ERROR, with NO space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"
# bash: !p': event not found


@Warren .. धन्यवाद। मैंने देखा था कि क्यूए, लेकिन यह वास्तव में केवल बैकस्लैश से बचने के तरीके के बारे में बात करता है ... मेरा मुद्दा इस बात से अधिक संबंधित है कि प्रतीत होता है कि पहले से ही
बची हुई

@ पसंदीदा: "पहले से ही बच गए"? मुझे कोई बचता नहीं दिख रहा है और आप दोहरे उद्धरण चिह्नों का उपयोग कर रहे हैं। मेरा (संशोधित) उत्तर देखें। आपको क्या लगता है कि कौन सा हिस्सा बच गया है?
कालेब

@Caleb। हां, मैंने गलत शब्द का इस्तेमाल किया है .. protectedऔर अधिक उपयुक्त होगा। ('सिंगल कोट्स' द्वारा संरक्षित)
पीटर।

यदि आप केवल सरल असाइनमेंट से संबंधित हैं, तो आप var=$(…)(कोई दोहरे उद्धरण नहीं) का उपयोग कर सकते हैं , और यह आपकी अपेक्षा के अनुसार काम करेगा (मुझे लगता है)। यह अभी भी "सुरक्षित" है, क्योंकि एक साधारण कार्य के मूल्य भाग शब्द बंटवारे के अधीन या ग्लोबिंग (हालांकि यह जैसे builtins के माध्यम से किया (कार्य की सच नहीं हो सकता नहीं है export, localआदि सभी के गोले के तहत))। दुर्भाग्य से, यह सरल असाइनमेंट से आगे नहीं बढ़ता है क्योंकि डबल कोट्स शब्द विभाजन और ग्लोबिंग से बचाने का तरीका है जबकि अन्य संदर्भों में अभी भी अन्य प्रकार का विस्तार हो रहा है।
क्रिस जॉन्सन

जवाबों:


12

यह !चरित्र बैश के इतिहास के प्रतिस्थापन को दर्शाता है। जब एक स्ट्रिंग द्वारा पीछा किया जाता है (आपके असफल उदाहरण के रूप में) तो यह उस स्ट्रिंग के साथ शुरू हुई अंतिम इतिहास घटना तक विस्तार करने की कोशिश करता है। जैसे $varउस स्ट्रिंग के मूल्य में !echoविस्तार होता है , वैसे ही आपके इतिहास में अंतिम इको कमांड का विस्तार होगा।

अंतरिक्ष ऐसे विस्तार में एक टूटने वाला चरित्र है। पहले ध्यान दें कि यह चर के साथ कैसे काम करेगा:

# var="like"
# echo "$var"
like
# echo "$"
$
# echo "Do you $var frogs?"
Do you like frogs?       <- as expected, variable name broken at space
# echo "Do you $varfrogs?"
Do you?                  <- $varfrogs not defined, replaced with blank
# echo "Do you $ var frogs?"
Do you $ var frogs?      <- $ not a valid variable name, ignored

यही बात इतिहास विस्तार के लिए भी होगी। धमाके का चरित्र ( !) एक इतिहास प्रतिस्थापन अनुक्रम शुरू करता है, लेकिन केवल अगर एक स्ट्रिंग द्वारा पीछा किया जाता है। एक स्थान के साथ इसका अनुसरण करना एक प्रतिस्थापित अनुक्रम के भाग के बजाय इसे शाब्दिक धमाका बनाता है

आप एकल उद्धरणों का उपयोग करके चर और इतिहास विस्तार दोनों के लिए इस तरह के प्रतिस्थापन से बच सकते हैं। आपके पहले उदाहरणों में सिंगल कोट्स का इस्तेमाल किया गया और इसलिए यह ठीक रहा। आपके अंतिम उदाहरण दोहरे उद्धरण चिह्नों में हैं और इस प्रकार उन्हें कुछ और करने से पहले विस्तार दृश्यों के लिए स्कैन किया गया। एकमात्र पहला कारण यह नहीं था कि अंतरिक्ष एक ब्रेक चरित्र है जैसा कि ऊपर दिखाया गया है।


धन्यवाद कालेब .. मेरी पूर्व धारणाओं में से एक को खारिज कर दिया गया ... मुझे लगा कि बैश परसिंग को अंतरतम ब्रैकेट या ब्रेस से किया गया था, और फिर बाहर की ओर काम किया ... ऐसा लगता है कि बैश मेरी धारणा से अलग है।
पीटर।

1
यह उद्धृत करने में बहुत भ्रामक है, क्योंकि यह नेस्टेड तारों में बदल रहा है। जैसा कि यह है, प्रतिस्थापन प्रक्रिया में बहुत जल्दी होते हैं। इस उदाहरण पर विचार करें:var=word; echo "test '$var'"; echo 'test "$var"'
कालेब

.. हाँ अनसुना। मुझे उद्धरणों के भीतर उद्धरण चिह्नों के बारे में पता था ... मेरी गलतफहमी यह थी कि मैंने सोचा था कि कमांड प्रतिस्थापन के कोष्ठक के भीतर कोड को अलग से पार्स किया जाएगा, जो उन ब्रैकेट को घेरता है; लेकिन स्पष्ट रूप से नहीं .. धन्यवाद।
पीटर।

6

जैसा कि पहले से ही कालेब द्वारा कहा गया है , !का उपयोग बैश के इतिहास प्रतिस्थापन को लागू करने के लिए किया जाता है।

अगर मेरी तरह आपको लगता है कि आपको इस तरह की सुविधा की आवश्यकता नहीं है, तो आप इसे निम्न पंक्ति में सम्मिलित कर सकते हैं ~/.bashrc:

set +H

मुझे इसकी आवश्यकता नहीं है क्योंकि इतिहास को ऊपर तीर और Ctrl- rवृद्धिशील रिवर्स खोज द्वारा पुनर्प्राप्त किया जा सकता है । बैश के मैनुअल पेज, शॉर्टकट्स की विस्तृत सूची के लिए इतिहास को हेरफेर करने के लिए अनुभाग कमांड देखें ।


2
आप बिना कैसे रहते हैं !!?
कालेब

धन्यवाद।, मुझे लगता है कि यह एक समस्या हो सकती है, पोर्टेबिलिटी-वार, लेकिन set +Hस्क्रिप्ट में काम करना वैसे ही काम करता है :) +1
पीटर।

2
@ पसंदीदा: अजीब, आम तौर पर इतिहास का विस्तार केवल "इंटरेक्टिव गोले" के लिए होता है।
13

@enzo .. फिर से धन्यवाद .. मैंने कमांडलाइन से इसका परीक्षण किया था .. आह! अगर सीखना इतना मजेदार नहीं था, तो यह थकाऊ होगा ... क्या मैंने कॉफी का उल्लेख किया है? यह बहुत मदद करता है :)
पीटर।

हां, यह एक पकड़ है। मैंने स्क्रिप्ट्स से पेस्ट किए गए कोड को कॉपी किया है जो इस कारण से कमांड लाइन पर विफल रहे। इतिहास के विस्तार की स्क्रिप्ट में कोई चिंता नहीं थी लेकिन यह एक इंटरैक्टिव शेल पर है।
कालेब

2

आपका पहला उदाहरण:

{ echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }

को कम किया जा सकता है

echo '! p' 
echo '!p'

एकल उद्धरण के भीतर, सभी वर्ण अपने शाब्दिक मूल्यों को संरक्षित करते हैं। इस प्रकार !अपना विशेष अर्थ खो दिया है और इतिहास विस्तार पूर्वनिर्मित नहीं है।

आपके दूसरे और तीसरे उदाहरण:

var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"

var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"

को कम किया जा सकता है

echo "'! p'"

echo "'!p'"

'! p'और '!p'अनिवार्य रूप से दोहरे उद्धृत तारों के हिस्से हैं।

दोहरे उद्धरण चिह्नों के भीतर, सभी पात्रों उनके शाब्दिक मूल्यों की रक्षा सिवाय $ , `, \और !

इसका तात्पर्य एकल उद्धरणों से है '! p'और '!p'उन्होंने अपने विशेष अर्थ खो दिए हैं (अर्थात: भागने में असमर्थ !) लेकिन फिर !भी इसके विशेष अर्थ को बरकरार रखता है इस प्रकार इतिहास का विस्तार किया जाता है।

हालांकि, जब !एक अंतरिक्ष चरित्र द्वारा पीछा किया जाता है, तो इतिहास का विस्तार नहीं किया जाता है।

से उद्धृत man bash:

का हवाला देते हुए

[...]

एकल उद्धरणों में वर्णों को शामिल करना उद्धरणों के भीतर प्रत्येक वर्ण के शाब्दिक मूल्य को संरक्षित करता है। [...]

दोहरे उद्धरण चिह्नों में संलग्न करना, उद्धरणों के भीतर सभी वर्णों के शाब्दिक मूल्य को, $, `, \, और, के अपवाद के साथ रखता है, जब इतिहास विस्तार सक्षम होता है ;। [...] यदि सक्षम किया गया, तो इतिहास विस्तार तब तक किया जाएगा जब तक कि ए! दोहरे उद्धरण चिह्नों में दिखाई देने से बैकस्लैश का उपयोग करके बच जाता है। बैकस्लैश से पहले! हटाया नहीं गया है।

इतिहास विस्तार

[...]

इतिहास विस्तार का परिचय इतिहास विस्तार चरित्र की उपस्थिति से होता है, जो कि है! डिफ़ॉल्ट रूप से। केवल बैकस्लैश (\) और एकल उद्धरण इतिहास विस्तार वर्ण को उद्धृत कर सकते हैं।

इतिहास विस्तार वर्ण का अनुसरण करते हुए पाए जाने पर कई वर्ण इतिहास विस्तार को रोक देते हैं, भले ही वह निर्विरोध हो: अंतरिक्ष, टैब, न्यूलाइन, गाड़ी वापसी, और =। यदि extglob शेल विकल्प सक्षम है, (विस्तार को भी रोक देगा।

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