मैं एक पर्यावरण चर क्यों नहीं निर्दिष्ट कर सकता और उसी कमांड लाइन में इसे प्रतिध्वनित कर सकता हूं?


91

इस स्निपेट पर विचार करें:

$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz

यहाँ मैं निर्धारित किया है $SOMEVARकरने के लिए AAAपहली पंक्ति पर - और जब मैं दूसरी पंक्ति पर यह गूंज, मैं AAAसामग्री के रूप में उम्मीद।

लेकिन फिर, अगर मैं उसी कमांड लाइन पर चर को निर्दिष्ट करने का प्रयास करता हूं echo:

$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz

... BBBजैसा मुझे उम्मीद थी वैसा नहीं मिला - मुझे पुराना मूल्य ( AAA) मिला।

क्या यह है कि चीजों को कैसे माना जाता है? यदि हां, तो कैसे आए तो आप चर को निर्दिष्ट कर सकते हैं LD_PRELOAD=/... program args ...और यह काम कर सकता है? मैं क्या खो रहा हूँ?


2
यह तब काम करता है जब आप असाइनमेंट को एक अलग स्टेटमेंट बनाते हैं, या किसी स्क्रिप्ट को अपने परिवेश के साथ इनवॉइस करते हैं, लेकिन वर्तमान परिवेश में एक कमांड को पूर्वस्थापित करते समय नहीं। दिलचस्प!
टॉड ए। जैकब

1
कारण LD_PRELOADकाम करता है कि चर कार्यक्रम के दशक में सेट कर दिया जाता है पर्यावरण - नहीं अपने कमांड लाइन पर।
अगली सूचना तक रोक दिया गया।

जवाबों:


103

आप जो देख रहे हैं वह अपेक्षित व्यवहार है। मुसीबत यह है कि $SOMEVARसंशोधित वातावरण के साथ कमांड को लागू करने से पहले पैरेंट शेल कमांड लाइन पर मूल्यांकन करता है। $SOMEVARपर्यावरण के सेट होने तक आपको आस्थगित का मूल्यांकन प्राप्त करने की आवश्यकता है।

आपके तत्काल विकल्पों में शामिल हैं:

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
  2. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'

माता-पिता के खोल का मूल्यांकन करने से रोकने के लिए ये दोनों एकल उद्धरण का उपयोग करते हैं $SOMEVAR; यह केवल पर्यावरण में सेट होने के बाद (अस्थायी रूप से, एकल आदेश की अवधि के लिए) मूल्यांकन किया जाता है।

एक अन्य विकल्प उप-खोल संकेतन का उपयोग करना है (जैसा कि उनके जवाब में मार्कस कुह्न द्वारा सुझाया गया है ):

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

चर केवल उप-शेल में सेट किया गया है


बहुत बढ़िया, @JonathanLeffler - स्पष्टीकरण के लिए बहुत धन्यवाद; चियर्स!
सादाऊ

@ Markus-kuhn से आगे बढ़ना मुश्किल है।
एलेक्स चे

37

समस्या, पुनरीक्षित

स्पष्ट रूप से, इस बिंदु पर मैनुअल भ्रमित है। जीएनयू बैश मैनुअल का कहना है:

किसी भी साधारण कमांड या फंक्शन के लिए पर्यावरण [ध्यान दें कि यह बिलिंस को बाहर करता है] इसे शेल पैरामीटर में वर्णित के रूप में पैरामीटर असाइनमेंट के साथ प्रीफ़िक्स करके अस्थायी रूप से संवर्धित किया जा सकता है। ये असाइनमेंट स्टेटमेंट केवल उस कमांड द्वारा देखे गए पर्यावरण को प्रभावित करते हैं।

यदि आप वास्तव में वाक्य को पार्स करते हैं, तो यह क्या कह रहा है कि कमांड / फ़ंक्शन के लिए वातावरण संशोधित किया गया है, लेकिन मूल प्रक्रिया के लिए वातावरण नहीं। तो, यह काम करेगा:

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

क्योंकि env कमांड का वातावरण निष्पादित होने से पहले संशोधित किया गया है। हालांकि, यह काम नहीं करेगा:

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

जब पैरामीटर विस्तार शेल द्वारा किया जाता है।

दुभाषिया कदम

समस्या का एक और हिस्सा यह है कि बैश अपने दुभाषिया के लिए इन चरणों को परिभाषित करता है :

  1. एक फ़ाइल (शेल लिपियों को देखें) से इसके इनपुट को पढ़ता है, एक स्ट्रिंग से एक तर्क के रूप में आपूर्ति की जाती है -c मंगलाचरण विकल्प (इनवॉइस बैश देखें), या उपयोगकर्ता के टर्मिनल से।
  2. कोटिंग में वर्णित उद्धरण नियमों का पालन करते हुए, शब्दों और ऑपरेटरों में इनपुट को तोड़ता है। इन टोकन को मेटाचैटर्स द्वारा अलग किया जाता है। इस कदम से एलियास का विस्तार होता है (एलियासेस देखें)।
  3. टोकन को सरल और यौगिक कमांड में देखें (शेल कमांड देखें)।
  4. विभिन्न शेल विस्तार (शैल विस्तार देखें) को करता है, विस्तारित टोकन को फाइलनाम की सूची में तोड़ता है (फाइलनाम विस्तार देखें) और आदेश और तर्क।
  5. कोई आवश्यक पुनर्निर्देशन करता है (पुनर्निर्देशन देखें) और पुनर्निर्देशन ऑपरेटरों और उनके ऑपरेटरों को तर्क सूची से निकाल देता है।
  6. आदेश निष्पादित करता है (निष्पादन कमांड देखें)।
  7. वैकल्पिक रूप से कमांड के पूरा होने की प्रतीक्षा करता है और अपनी निकास स्थिति को एकत्रित करता है (एक्ज़िट स्थिति देखें)।

यहाँ क्या हो रहा है कि बिल्डरों को अपना निष्पादन वातावरण नहीं मिलता है, इसलिए वे कभी भी संशोधित वातावरण नहीं देखते हैं। इसके अलावा, सरल कमांड (जैसे / बिन / प्रतिध्वनि) को एक संशोधित एनीवेंशन मिलता है (इसीलिए env उदाहरण ने काम किया है) लेकिन शेल का विस्तार मौजूदा वातावरण में चरण # 4 में हो रहा है।

दूसरे शब्दों में, आप 'aaa $ TESTVAR ccc' को / bin / echo में पास नहीं कर रहे हैं; आप प्रक्षेपित स्ट्रिंग (वर्तमान परिवेश में विस्तारित) / बिन / प्रतिध्वनि में पास कर रहे हैं। इस स्थिति में, चूंकि वर्तमान परिवेश में कोई TESTVAR नहीं है , इसलिए आप केवल कमांड में ' aa ccc' पास कर रहे हैं।

सारांश

प्रलेखन बहुत स्पष्ट हो सकता है। अच्छी बात यह है कि ढेर अतिप्रवाह है!

यह सभी देखें

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment


मैंने पहले ही इसे उखाड़ फेंका था - लेकिन मैं अभी इस प्रश्न पर वापस आया था, और इस पोस्ट में ठीक उसी बिंदु को समाहित किया गया है जिसकी मुझे आवश्यकता है; बहुत धन्यवाद, @CodeGnome!
सादाउ

मुझे नहीं पता कि इस जवाब के पोस्ट होने के बाद से बैश इस क्षेत्र में बदल गया है, लेकिन उपसर्ग किए गए चर असाइनमेंट अब बिल्डिंस के साथ काम करते हैं। उदाहरण के लिए, FOO=foo eval 'echo $FOO'प्रिंट fooउम्मीद के मुताबिक । इसका मतलब है कि आप इस तरह की चीजें कर सकते हैं IFS="..." read ...
विल वुडेन

मुझे लगता है कि यह क्या हो रहा है कि बैश वास्तव में अस्थायी रूप से अपने स्वयं के वातावरण को संशोधित करता है, और कमांड पूरा होने के बाद इसे पुनर्स्थापित करता है, जिसमें अजीब दुष्प्रभाव हो सकते हैं।
विल वुडेन

22

जो आप चाहते हैं, उसे प्राप्त करने के लिए

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

कारण:

  • आपको अगले कमांड से अर्धविराम या नई लाइन द्वारा असाइनमेंट को अलग करना होगा, अन्यथा अगले कमांड (इको) के लिए पैरामीटर विस्तार होने से पहले इसे निष्पादित नहीं किया जाता है ।

  • आपको यह सुनिश्चित करने के लिए कि यह वर्तमान लाइन से परे बनी नहीं है, एक उप- वातावरण में असाइनमेंट बनाने की आवश्यकता है ।

यह समाधान सुझाए गए कुछ अन्य लोगों की तुलना में छोटा, नट और अधिक कुशल है, विशेष रूप से यह एक नई प्रक्रिया नहीं बनाता है।


3
भविष्य के गोगलर्स के लिए जो यहाँ हवा देते हैं: यह शायद इस सवाल का सबसे अच्छा जवाब है। इसे और जटिल करने के लिए, यदि आपको कमांड के वातावरण में उपलब्ध असाइनमेंट की आवश्यकता है, तो आपको इसे निर्यात करने की आवश्यकता है। उपधारा अभी भी कार्य को जारी रखने से रोकती है। (export SOMEVAR=BBB; python -c "from os import getenv; print getenv('SOMEVAR')")
ईज

@eaj आपके उदाहरण के अनुसार, एक एकल बाहरी प्रोग्राम कॉल के लिए शेल चर निर्यात करने के लिए, बस का उपयोग करेंSOMEVAR=BBB python -c "from os import getenv; print getenv('SOMEVAR')"
Markus Kuhn

10

कारण यह है कि यह एक लाइन के लिए एक पर्यावरण चर सेट करता है। लेकिन, echoविस्तार नहीं bashकरता , करता है। इसलिए, अपने चर वास्तव में विस्तार से पहले आदेश निष्पादित किया जाता है, भले ही है SOME_VARहै BBBगूंज आदेश के संदर्भ में।

प्रभाव को देखने के लिए, आप कुछ ऐसा कर सकते हैं:

$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB

यहां चर का विस्तार तब तक नहीं किया जाता है जब तक कि बच्चे की प्रक्रिया निष्पादित नहीं हो जाती है, इसलिए आप अद्यतन मूल्य देखते हैं। यदि आप SOME_VARIABLEमूल शेल में फिर से जांच करते हैं, तो यह अभी भी AAAअपेक्षित है।


1
+1 के सही स्पष्टीकरण के लिए कि यह लिखित रूप में क्यों काम नहीं करता है, और व्यवहार्य वर्कअराउंड के लिए।
जोनाथन लेफ़लर

1
SOMEVAR=BBB; echo zzz $SOMEVAR zzz

का उपयोग ; एक ही लाइन पर हैं कि अलग बयानों के लिए।


1
यह काम करता है, लेकिन यह काफी हद तक नहीं है। विचार सिर्फ एक आदेश के लिए पर्यावरण को निर्धारित करना है, न कि स्थायी रूप से जैसा कि आपका समाधान करता है।
जोनाथन लेफ़लर

उस @Kyros के लिए धन्यवाद; पता नहीं कैसे मुझे याद आया कि अब तक :) अभी भी भटक रहा है कि कैसे LD_PRELOADऔर इस तरह एक अर्धविराम के बिना एक निष्पादन योग्य के सामने काम कर सकता है, हालांकि ... बहुत धन्यवाद फिर से - चीयर्स!
सादाऊ

@JonathanLeffler - वास्तव में, यह विचार था; मुझे महसूस नहीं हुआ कि अर्धविराम बदलाव को स्थायी बनाता है - ध्यान देने के लिए धन्यवाद!
सादाऊ

1

यहाँ एक विकल्प है:

SOMEVAR=BBB && echo zzz $SOMEVAR zzz

चाहे आप कमांड का उपयोग करें &&या ;अलग करने के लिए, असाइनमेंट बनी रहती है, जो ओपी का वांछित व्यवहार नहीं है। मार्कस कुह्न के पास इस उत्तर का सही संस्करण है।
14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.