कैसे बास में एक समारोह के भीतर एक वैश्विक चर को संशोधित करने के लिए?


104

मैं इसके साथ काम कर रहा हूँ:

GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

मेरे पास नीचे की तरह एक स्क्रिप्ट है:

#!/bin/bash

e=2

function test1() {
  e=4
  echo "hello"
}

test1 
echo "$e"

कौन सा रिटर्न:

hello
4

लेकिन अगर मैं फ़ंक्शन का परिणाम किसी वैरिएबल को सौंपता हूं, तो वैश्विक वैरिएबल eसंशोधित नहीं है:

#!/bin/bash

e=2

function test1() {
  e=4
  echo "hello"
}

ret=$(test1)

echo "$ret"
echo "$e"

यह दिखाता है:

hello
2

मैंने इस मामले में निष्कासन के उपयोग के बारे में सुना है , इसलिए मैंने ऐसा किया है test1:

eval 'e=4'

लेकिन वही नतीजा।

क्या आप मुझे समझा सकते हैं कि इसे संशोधित क्यों नहीं किया गया है? मैं test1समारोह की गूंज को कैसे बचा सकता हूं retऔर वैश्विक चर को भी संशोधित कर सकता हूं ?


क्या आपको नमस्ते वापस करने की आवश्यकता है? आप इसे वापस करने के लिए बस $ e को प्रतिध्वनित कर सकते हैं। या सब कुछ जो आप चाहते हैं और फिर परिणाम को प्रतिध्वनित करें?

जवाबों:


98

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

myfunc() {
    echo "Hello"
}

var="$(myfunc)"

echo "$var"

देता है:

Hello

0-255 से संख्यात्मक मान के लिए, आप returnसंख्या को निकास स्थिति के रूप में पास करने के लिए उपयोग कर सकते हैं :

mysecondfunc() {
    echo "Hello"
    return 4
}

var="$(mysecondfunc)"
num_var=$?

echo "$var - num is $num_var"

देता है:

Hello - num is 4

बिंदु के लिए धन्यवाद, लेकिन मुझे एक स्ट्रिंग सरणी वापस करना होगा, और फ़ंक्शन के भीतर मुझे तत्वों को दो वैश्विक स्ट्रिंग सरणियों में जोड़ना होगा।
harrison4

3
आपको पता चलता है कि यदि आप फ़ंक्शन को केवल एक वेरिएबल को असाइन किए बिना ही चलाते हैं, तो उसके भीतर के सभी ग्लोबल वैरिएबल अपडेट हो जाएंगे। स्ट्रिंग सरणी वापस करने के बजाय, फ़ंक्शन में स्ट्रिंग सरणी को केवल अपडेट क्यों न करें, फिर फ़ंक्शन समाप्त होने के बाद इसे दूसरे चर पर असाइन करें?

@ जॉनडे: आप एक फ़ंक्शन से "स्ट्रिंग ऐरे" नहीं लौटा सकते। आप केवल एक स्ट्रिंग प्रिंट कर सकते हैं। : हालांकि, अगर आप इस तरह कुछ कर सकते हैंsetarray() { declare -ag "$1=(a b c)"; }
rici

34

इस जरूरतों 4.1 पार्टी की योजना बनाई है, तो आप का उपयोग करें {fd}या local -n

बाकी मैं 3.x में आशा है कि काम करना चाहिए। मैं पूरी तरह से सुनिश्चित नहीं हूं printf %q- यह एक बैश 4 फीचर हो सकता है।

सारांश

वांछित प्रभाव को संग्रहीत करने के लिए आपका उदाहरण निम्नानुसार संशोधित किया जा सकता है:

# Add following 4 lines:
_passback() { while [ 1 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; return $1; }
passback() { _passback "$@" "$?"; }
_capture() { { out="$("${@:2}" 3<&-; "$2_" >&3)"; ret=$?; printf "%q=%q;" "$1" "$out"; } 3>&1; echo "(exit $ret)"; }
capture() { eval "$(_capture "$@")"; }

e=2

# Add following line, called "Annotation"
function test1_() { passback e; }
function test1() {
  e=4
  echo "hello"
}

# Change following line to:
capture ret test1 

echo "$ret"
echo "$e"

प्रिंट के रूप में वांछित:

hello
4

ध्यान दें कि यह समाधान:

  • के लिए e=1000भी काम करता है।
  • $?जरूरत पड़ने पर संरक्षित करता है$?

केवल बुरे दुष्प्रभाव हैं:

  • इसे आधुनिक की जरूरत है bash
  • यह काफी अधिक बार कांटे देता है।
  • इसे एनोटेशन की आवश्यकता है (आपके फ़ंक्शन के नाम पर, एक जोड़ा के साथ _)
  • यह फाइल डिस्क्रिप्टर 3 का बलिदान करता है।
    • जरूरत पड़ने पर आप इसे दूसरी एफडी में बदल सकते हैं।
      • में _captureबस की सभी आवृत्तियां की जगह 3एक और (उच्च) संख्या के साथ।

निम्नलिखित (जो कि काफी लंबा है, उसके लिए खेद है) उम्मीद करता है कि इस नुस्खे को अन्य लिपियों के लिए भी स्वीकार किया जाए।

समस्या

d() { let x++; date +%Y%m%d-%H%M%S; }

x=0
d1=$(d)
d2=$(d)
d3=$(d)
d4=$(d)
echo $x $d1 $d2 $d3 $d4

आउटपुट

0 20171129-123521 20171129-123521 20171129-123521 20171129-123521

जबकि वांछित आउटपुट है

4 20171129-123521 20171129-123521 20171129-123521 20171129-123521

समस्या का कारण

शैल चर (या आम तौर पर बोल, पर्यावरण) को माता-पिता की प्रक्रियाओं से बाल प्रक्रियाओं में पारित किया जाता है, लेकिन इसके विपरीत नहीं।

यदि आप आउटपुट कैप्चरिंग करते हैं, तो यह आमतौर पर एक सब-टाइम में चलाया जाता है, इसलिए बैक चर को पार करना मुश्किल है।

कुछ आपको यह भी बताते हैं, कि इसे ठीक करना असंभव है। यह गलत है, लेकिन समस्या को हल करने के लिए यह लंबे समय से ज्ञात मुश्किल है।

इसे सर्वोत्तम तरीके से हल करने के कई तरीके हैं, यह आपकी आवश्यकताओं पर निर्भर करता है।

यह कैसे करना है पर एक कदम गाइड द्वारा कदम है।

पैतृक खोल में चर चरना

पैतृक खोल के लिए चर को वापस करने का एक तरीका है। हालाँकि यह एक खतरनाक रास्ता है, क्योंकि यह उपयोग करता है eval। यदि अनुचित तरीके से किया जाता है, तो आप कई बुरी चीजों का जोखिम उठाते हैं। लेकिन अगर ठीक से किया जाए, तो यह पूरी तरह से सुरक्षित है, बशर्ते कि इसमें कोई बग न हो bash

_passback() { while [ 0 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; }

d() { let x++; d=$(date +%Y%m%d-%H%M%S); _passback x d; }

x=0
eval `d`
d1=$d
eval `d`
d2=$d
eval `d`
d3=$d
eval `d`
d4=$d
echo $x $d1 $d2 $d3 $d4

प्रिंट

4 20171129-124945 20171129-124945 20171129-124945 20171129-124945

ध्यान दें कि यह खतरनाक चीजों के लिए भी काम करता है:

danger() { danger="$*"; passback danger; }
eval `danger '; /bin/echo *'`
echo "$danger"

प्रिंट

; /bin/echo *

यह इस कारण से है printf '%q', जो सब कुछ को उद्धृत करता है, ताकि आप इसे शेल संदर्भ में सुरक्षित रूप से फिर से उपयोग कर सकें।

लेकिन यह एक दर्द में है ..

यह न केवल बदसूरत दिखता है, यह भी टाइप करना बहुत है, इसलिए यह त्रुटि प्रवण है। बस एक ही गलती और आप बर्बाद हैं, है ना?

खैर, हम शेल स्तर पर हैं, इसलिए आप इसे सुधार सकते हैं। बस एक इंटरफ़ेस के बारे में सोचें जिसे आप देखना चाहते हैं, और फिर आप इसे लागू कर सकते हैं।

ऑगमेंट, शेल कैसे चीजों को प्रोसेस करता है

चलो एक कदम पीछे जाते हैं और कुछ एपीआई के बारे में सोचते हैं जो हमें आसानी से व्यक्त करने की अनुमति देता है, हम क्या करना चाहते हैं।

खैर, हम d()फ़ंक्शन के साथ क्या करना चाहते हैं ?

हम आउटपुट को वेरिएबल में कैप्चर करना चाहते हैं। ठीक है, तो चलिए इसके लिए एक एपीआई लागू करें:

# This needs a modern bash 4.3 (see "help declare" if "-n" is present,
# we get rid of it below anyway).
: capture VARIABLE command args..
capture()
{
local -n output="$1"
shift
output="$("$@")"
}

अब लिखने के बजाय

d1=$(d)

हम लिख सकते है

capture d1 d

ठीक है, ऐसा लगता है कि हम बहुत बदल नहीं गए हैं, जैसा कि, फिर से, चर dमाता-पिता के खोल से वापस पारित नहीं किए गए हैं , और हमें थोड़ा और टाइप करने की आवश्यकता है।

हालाँकि अब हम इस पर शेल की पूरी शक्ति फेंक सकते हैं, क्योंकि यह अच्छी तरह से एक फ़ंक्शन में लिपटा हुआ है।

इंटरफ़ेस का पुन: उपयोग करने के लिए एक आसान के बारे में सोचो

एक दूसरी बात यह है, कि हम DRY होना चाहते हैं (खुद को दोहराना नहीं)। इसलिए हम निश्चित रूप से कुछ टाइप नहीं करना चाहते हैं

x=0
capture1 x d1 d
capture1 x d2 d
capture1 x d3 d
capture1 x d4 d
echo $x $d1 $d2 $d3 $d4

xयहां न केवल अनावश्यक, यह त्रुटि हमेशा सही संदर्भ में repeate की संभावना है। क्या होगा यदि आप इसे स्क्रिप्ट में 1000 बार उपयोग करते हैं और फिर एक चर जोड़ते हैं? आप निश्चित रूप से उन सभी 1000 स्थानों को बदलना नहीं चाहते जहाँ कॉल करना dशामिल है।

इसलिए xदूर छोड़ दो , तो हम लिख सकते हैं:

_passback() { while [ 0 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; }

d() { let x++; output=$(date +%Y%m%d-%H%M%S); _passback output x; }

xcapture() { local -n output="$1"; eval "$("${@:2}")"; }

x=0
xcapture d1 d
xcapture d2 d
xcapture d3 d
xcapture d4 d
echo $x $d1 $d2 $d3 $d4

आउटपुट

4 20171129-132414 20171129-132414 20171129-132414 20171129-132414

यह पहले से ही बहुत अच्छा लग रहा है। (लेकिन अभी भी है local -nजो आम bash3.x में काम नहीं करता है )

बदलने से बचें d()

अंतिम समाधान में कुछ बड़ी खामियां हैं:

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

क्या हम इससे भी छुटकारा पा सकते हैं?

बिलकुल हम कर सकते हैं! हम एक शेल में हैं, इसलिए ऐसा करने के लिए हमें जो कुछ भी चाहिए वह है।

यदि आप कॉल को थोड़ा करीब से evalदेख सकते हैं, तो आप देख सकते हैं कि इस स्थान पर हमारा 100% नियंत्रण है। "अंदर" evalहम एक उपधारा में हैं, इसलिए हम माता-पिता के खोल में कुछ बुरा करने के डर के बिना हम सब कुछ कर सकते हैं।

हाँ, अच्छा है, तो चलो एक और आवरण जोड़ते हैं, अब सीधे अंदर हैं eval:

_passback() { while [ 0 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; }
# !DO NOT USE!
_xcapture() { "${@:2}" > >(printf "%q=%q;" "$1" "$(cat)"); _passback x; }  # !DO NOT USE!
# !DO NOT USE!
xcapture() { eval "$(_xcapture "$@")"; }

d() { let x++; date +%Y%m%d-%H%M%S; }

x=0
xcapture d1 d
xcapture d2 d
xcapture d3 d
xcapture d4 d
echo $x $d1 $d2 $d3 $d4

प्रिंट

4 20171129-132414 20171129-132414 20171129-132414 20171129-132414                                                    

हालाँकि, यह, फिर से, कुछ बड़ी खामी है:

  • !DO NOT USE!मार्कर, देखते हैं इस में एक बहुत ही बुरा रेस स्थिति है, जो आप आसानी से नहीं देख सकता है, क्योंकि:
    • >(printf ..)एक पृष्ठभूमि काम है। तो यह अभी भी _passback xचल रहा है जबकि निष्पादित हो सकता है।
    • यदि आप sleep 1;पहले printfया जोड़ते हैं तो आप इसे स्वयं देख सकते हैं _passback_xcapture a d; echoफिर आउटपुट xया aपहले, क्रमशः।
  • का _passback xहिस्सा नहीं होना चाहिए _xcapture, क्योंकि इससे उस नुस्खा का पुन: उपयोग करना मुश्किल हो जाता है।
  • इसके अलावा, हमारे पास यहाँ ( $(cat)) कुछ अनकैप्ड फोर्क हैं , लेकिन जैसा कि इस समाधान !DO NOT USE!मैं सबसे छोटा रास्ता है।

हालाँकि, यह दिखाता है, कि हम यह कर सकते हैं, संशोधन के बिना d()(और बिना local -n)!

कृपया ध्यान दें कि हमें बिल्कुल भी ज़रूरत नहीं है _xcapture, क्योंकि हम हर सही में लिख सकते हैं eval

हालाँकि ऐसा करना आमतौर पर बहुत पठनीय नहीं है। और यदि आप कुछ वर्षों में अपनी स्क्रिप्ट पर वापस आते हैं, तो आप शायद इसे बहुत परेशानी के बिना फिर से पढ़ना चाहते हैं।

दौड़ को ठीक करें

अब चलिए दौड़ की हालत ठीक करते हैं।

चाल तब तक इंतजार कर सकती है जब तक printfकि यह STDOUT बंद न हो जाए , और फिर आउटपुट x

इसे संग्रहीत करने के कई तरीके हैं:

  • आप शेल पाइप का उपयोग नहीं कर सकते, क्योंकि पाइप विभिन्न प्रक्रियाओं में चलते हैं।
  • एक अस्थायी फ़ाइलों का उपयोग कर सकते हैं,
  • या लॉक फाइल या फिफो जैसी कोई चीज। यह लॉक या फीफो के लिए प्रतीक्षा करने की अनुमति देता है,
  • या विभिन्न चैनल, सूचना को आउटपुट करने के लिए, और फिर कुछ सही क्रम में आउटपुट को इकट्ठा करते हैं।

अंतिम रास्ता निम्नलिखित लग सकता है (ध्यान दें कि यह printfअंतिम करता है क्योंकि यह यहां बेहतर काम करता है):

_passback() { while [ 0 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; }

_xcapture() { { printf "%q=%q;" "$1" "$("${@:2}" 3<&-; _passback x >&3)"; } 3>&1; }

xcapture() { eval "$(_xcapture "$@")"; }

d() { let x++; date +%Y%m%d-%H%M%S; }

x=0
xcapture d1 d
xcapture d2 d
xcapture d3 d
xcapture d4 d
echo $x $d1 $d2 $d3 $d4

आउटपुट

4 20171129-144845 20171129-144845 20171129-144845 20171129-144845

यह सही क्यों है?

  • _passback x सीधे STDOUT से बात करता है।
  • हालाँकि, जैसा कि STDOUT को आंतरिक कमांड में कैप्चर करने की आवश्यकता है, हम पहले इसे '3> और 1' के साथ FD3 (आप दूसरों का उपयोग कर सकते हैं) में "सेव" करते हैं और फिर इसके साथ पुन: उपयोग करते हैं >&3
  • के $("${@:2}" 3<&-; _passback x >&3)बाद खत्म होता है _passback, जब उपधारा STDOUT को बंद कर देता है।
  • तो printfइससे पहले ऐसा नहीं हो सकता _passback, भले ही कितना समय लगे _passback
  • ध्यान दें कि printfकमांड को पूरा कमांडलाइन इकट्ठा होने से पहले निष्पादित नहीं किया गया है, इसलिए हम printfस्वतंत्र रूप से कैसे printfकार्यान्वित किए जाते हैं , से कलाकृतियों को नहीं देख सकते हैं ।

इसलिए पहले _passbackनिष्पादित करता है, फिर printf

यह रेस को हल करता है, एक निश्चित फाइल डिस्क्रिप्टर का त्याग करते हुए 3. आप निश्चित रूप से, मामले में एक और फाइल डिस्क्रिप्टर का चयन कर सकते हैं, जो कि FD3 आपकी शेलस्क्रिप्ट में मुक्त नहीं है।

कृपया यह भी ध्यान दें कि 3<&-जो एफडी 3 को कार्य करने के लिए सुरक्षित रखता है।

इसे और सामान्य बनाएं

_captureइसमें ऐसे भाग शामिल हैं, जो d()एक पुन: प्रयोज्यता के दृष्टिकोण से खराब हैं। इसे कैसे हल करें?

ठीक है, इसे एक और चीज, एक अतिरिक्त कार्य, जो सही चीजों को वापस करना चाहिए, का परिचय देकर इसे उच्छृंखल तरीके से करें, जिसे मूल कार्य के साथ _संलग्न किया गया है।

यह फ़ंक्शन वास्तविक फ़ंक्शन के बाद कहा जाता है, और चीजों को बढ़ा सकता है। इस तरह, इसे कुछ एनोटेशन के रूप में पढ़ा जा सकता है, इसलिए यह बहुत पठनीय है:

_passback() { while [ 0 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; }
_capture() { { printf "%q=%q;" "$1" "$("${@:2}" 3<&-; "$2_" >&3)"; } 3>&1; }
capture() { eval "$(_capture "$@")"; }

d_() { _passback x; }
d() { let x++; date +%Y%m%d-%H%M%S; }

x=0
capture d1 d
capture d2 d
capture d3 d
capture d4 d
echo $x $d1 $d2 $d3 $d4

अभी भी प्रिंट करता है

4 20171129-151954 20171129-151954 20171129-151954 20171129-151954

रिटर्न-कोड तक पहुंच की अनुमति दें

केवल गुमशुदा है:

v=$(fn)$?जो fnलौटा वह सेट । तो आप शायद यही चाहते हैं। हालांकि इसके लिए कुछ बड़े बदलाव की जरूरत है:

# This is all the interface you need.
# Remember, that this burns FD=3!
_passback() { while [ 1 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; return $1; }
passback() { _passback "$@" "$?"; }
_capture() { { out="$("${@:2}" 3<&-; "$2_" >&3)"; ret=$?; printf "%q=%q;" "$1" "$out"; } 3>&1; echo "(exit $ret)"; }
capture() { eval "$(_capture "$@")"; }

# Here is your function, annotated with which sideffects it has.
fails_() { passback x y; }
fails() { x=$1; y=69; echo FAIL; return 23; }

# And now the code which uses it all
x=0
y=0
capture wtf fails 42
echo $? $x $y $wtf

प्रिंट

23 42 69 FAIL

अभी भी बहुत कुछ सुधार की गुंजाइश है

  • _passback() के साथ समाप्त किया जा सकता है passback() { set -- "$@" "$?"; while [ 1 -lt $# ]; do printf '%q=%q;' "$1" "${!1}"; shift; done; return $1; }
  • _capture() के साथ समाप्त किया जा सकता है capture() { eval "$({ out="$("${@:2}" 3<&-; "$2_" >&3)"; ret=$?; printf "%q=%q;" "$1" "$out"; } 3>&1; echo "(exit $ret)")"; }

  • समाधान फ़ाइल डिस्क्रिप्टर (यहां 3) को आंतरिक रूप से उपयोग करके प्रदूषित करता है। यदि आप एफडी पास करने के लिए होते हैं तो आपको इसे ध्यान में रखना चाहिए।
    ध्यान दें कि bash4.1 और ऊपर के {fd}कुछ अप्रयुक्त एफडी का उपयोग करना है।
    (शायद मैं अपने आसपास आने पर यहां एक समाधान जोड़ दूंगा।)
    ध्यान दें कि मैं इसे अलग-अलग कार्यों में लगाने के लिए उपयोग करता हूं _capture, क्योंकि इस सब को एक पंक्ति में भरना संभव है, लेकिन इसे पढ़ने और समझने के लिए तेजी से कठिन हो जाता है।

  • शायद आप कॉल किए गए फ़ंक्शन के STDERR को भी कैप्चर करना चाहते हैं। या आप एक से अधिक फ़ाइलेस्क्रिप्ट से और चरों से भी अंदर और बाहर जाना चाहते हैं।
    मेरे पास अभी तक कोई समाधान नहीं है, हालांकि यहां एक से अधिक एफडी को पकड़ने का एक तरीका है , इसलिए हम शायद चर को भी इस तरह से वापस कर सकते हैं।

यह भी न भूलें:

यह एक शेल फ़ंक्शन को कॉल करना चाहिए, न कि एक बाहरी कमांड।

बाहरी चर से बाहर पर्यावरण चर को पारित करने का कोई आसान तरीका नहीं है। (इसके साथ LD_PRELOAD=यह संभव होना चाहिए, हालांकि!) लेकिन यह तब कुछ पूरी तरह से अलग है।

आखरी श्ब्द

यह एकमात्र संभव समाधान नहीं है। यह एक समाधान के लिए एक उदाहरण है।

हमेशा आपके पास शेल में चीजों को व्यक्त करने के कई तरीके होते हैं। इसलिए बेहतरी के लिए बेझिझक और कुछ बेहतर पाएं।

यहाँ प्रस्तुत समाधान परिपूर्ण होने से काफी दूर है:

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

हालाँकि मुझे लगता है कि इसका उपयोग करना काफी आसान है:

  • "पुस्तकालय" की सिर्फ 4 पंक्तियों को जोड़ें।
  • अपने शेल फ़ंक्शन के लिए "एनोटेशन" की सिर्फ 1 पंक्ति जोड़ें।
  • अस्थायी रूप से केवल एक फ़ाइल विवरणक बलिदान।
  • और प्रत्येक चरण को वर्षों बाद भी समझना आसान होना चाहिए।

2
आप कमाल हैं
एलिरन मलका

14

हो सकता है कि आप किसी फाइल का उपयोग कर सकते हैं, फंक्शन के अंदर फाइल करने के लिए लिखें, उसके बाद फाइल से पढ़ें। मैं eएक सरणी में बदल गया हूं । इस उदाहरण में सरणी को वापस पढ़ते समय रिक्त को विभाजक के रूप में उपयोग किया जाता है।

#!/bin/bash

declare -a e
e[0]="first"
e[1]="secondddd"

function test1 () {
 e[2]="third"
 e[1]="second"
 echo "${e[@]}" > /tmp/tempout
 echo hi
}

ret=$(test1)

echo "$ret"

read -r -a e < /tmp/tempout
echo "${e[@]}"
echo "${e[0]}"
echo "${e[1]}"
echo "${e[2]}"

आउटपुट:

hi
first second third
first
second
third

13

आप क्या कर रहे हैं, आप test1 निष्पादित कर रहे हैं

$(test1)

एक उप-शेल (चाइल्ड शेल) और चाइल्ड शेल माता-पिता में कुछ भी संशोधित नहीं कर सकते हैं

आप इसे बाश मैनुअल में पा सकते हैं

कृपया जाँच करें: चीज़ें यहाँ एक उप-संस्करण में परिणाम करती हैं


7

मुझे इसी तरह की समस्या थी, जब मैं अपने द्वारा बनाई गई अस्थायी फ़ाइलों को स्वचालित रूप से निकालना चाहता था। मैं जिस समाधान के साथ आया था वह कमांड प्रतिस्थापन का उपयोग करने के लिए नहीं था, बल्कि चर के नाम को पारित करने के लिए, जो अंतिम परिणाम को फ़ंक्शन में ले जाना चाहिए। उदाहरण के लिए

#! /bin/bash

remove_later=""
new_tmp_file() {
    file=$(mktemp)
    remove_later="$remove_later $file"
    eval $1=$file
}
remove_tmp_files() {
    rm $remove_later
}
trap remove_tmp_files EXIT

new_tmp_file tmpfile1
new_tmp_file tmpfile2

तो, आपके मामले में यह होगा:

#!/bin/bash

e=2

function test1() {
  e=4
  eval $1="hello"
}

test1 ret

echo "$ret"
echo "$e"

काम करता है और "वापसी मूल्य" पर कोई प्रतिबंध नहीं है।


1

ऐसा इसलिए है क्योंकि सबस्टेशन में कमांड प्रतिस्थापन किया जाता है, इसलिए जब सबस्क्राइब चर का वारिस करता है, तो सबस्क्रिप्शन समाप्त होने पर उनमें परिवर्तन खो जाते हैं।

संदर्भ :

कमांड प्रतिस्थापन , कोष्ठक के साथ समूहीकृत कमांड, और एसिंक्रोनस कमांड्स को उप-वातावरण में लागू किया जाता है जो शेल पर्यावरण का एक डुप्लिकेट है


@ जॉनडे मुझे यकीन नहीं है कि यह संभव है। आपको स्क्रिप्ट के अपने डिजाइन पर पुनर्विचार करना पड़ सकता है।
कुछ प्रोग्रामर

ओह, लेकिन मुझे एक फ़ंक्शन के भीतर एक वैश्विक सरणी को आश्वस्त करने की आवश्यकता है, यदि नहीं, तो मुझे बहुत सारे कोड को दोहराना होगा (फ़ंक्शन के कोड को दोहराएं -30 लाइनें- 15 बार-प्रति कॉल-)। कोई और रास्ता नहीं है, है ना?
harrison4

1

इस समस्या का एक समाधान, जटिल कार्यों को पेश किए बिना और मूल रूप से भारी रूप से संशोधित करने के लिए, एक अस्थायी फ़ाइल में मूल्य को संग्रहीत करना है और जरूरत पड़ने पर इसे पढ़ना / लिखना है।

इस दृष्टिकोण ने मुझे बहुत मदद की जब मुझे एक चमगादड़ परीक्षण मामले में कई बार बैश फ़ंक्शन का मजाक उड़ाना पड़ा।

उदाहरण के लिए, आप कर सकते हैं:

# Usage read_value path_to_tmp_file
function read_value {
  cat "${1}"
}

# Usage: set_value path_to_tmp_file the_value
function set_value {
  echo "${2}" > "${1}"
}
#----

# Original code:

function test1() {
  e=4
  set_value "${tmp_file}" "${e}"
  echo "hello"
}


# Create the temp file
# Note that tmp_file is available in test1 as well
tmp_file=$(mktemp)

# Your logic
e=2
# Store the value
set_value "${tmp_file}" "${e}"

# Run test1
test1

# Read the value modified by test1
e=$(read_value "${tmp_file}")
echo "$e"

दोष यह है कि आपको विभिन्न चर के लिए कई अस्थायी फ़ाइलों की आवश्यकता हो सकती है। और आपको syncएक लिखने और पढ़ने के संचालन के बीच डिस्क पर सामग्री को जारी रखने के लिए एक कमांड जारी करने की आवश्यकता हो सकती है ।


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