सहयोगी सरणियों से परे, बैश में गतिशील चर प्राप्त करने के कई तरीके हैं। ध्यान दें कि ये सभी तकनीकें जोखिम को प्रस्तुत करती हैं, जिन पर इस उत्तर के अंत में चर्चा की जाती है।
निम्नलिखित उदाहरणों में मैं मान लूंगा i=37कि आप उस चर का नाम देना चाहते हैं var_37जिसका प्रारंभिक मूल्य है lolilol।
विधि 1. एक "पॉइंटर" चर का उपयोग करना
आप केवल एक अप्रत्यक्ष चर में चर का नाम स्टोर कर सकते हैं, सी पॉइंटर के विपरीत नहीं। बैश तब अलियास किए गए चर को पढ़ने के लिए एक सिंटैक्स है : चर ${!name}के मूल्य तक फैलता है जिसका नाम चर का मूल्य है name। आप इसे दो-चरणीय विस्तार के रूप में सोच सकते हैं: ${!name}विस्तार करता है $var_37, जिसका विस्तार होता है lolilol।
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
दुर्भाग्य से, उपनामित चर को संशोधित करने के लिए कोई समकक्ष वाक्यविन्यास नहीं है । इसके बजाय, आप निम्न में से किसी एक ट्रिक से असाइनमेंट प्राप्त कर सकते हैं।
1 क। साथ सौंपनाeval
evalबुराई है, लेकिन हमारे लक्ष्य को प्राप्त करने का सबसे सरल और सबसे पोर्टेबल तरीका भी है। आपको असाइनमेंट के दाईं ओर ध्यान से बचना होगा, क्योंकि इसका मूल्यांकन दो बार किया जाएगा । ऐसा करने का एक आसान और व्यवस्थित तरीका पहले से दाहिने हाथ की ओर (या उपयोग करने के लिए printf %q) का मूल्यांकन करना है ।
और आपको मैन्युअल रूप से जांच करनी चाहिए कि बाएं हाथ की ओर एक वैध चर नाम है, या सूचकांक के साथ एक नाम (यदि यह था तो क्या है evil_code #?)। इसके विपरीत, नीचे दिए गए अन्य सभी तरीके इसे स्वचालित रूप से लागू करते हैं।
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
downsides:
- चर नाम की वैधता की जाँच नहीं करता है।
eval बुराई है।
eval बुराई है।
eval बुराई है।
1b। साथ सौंपनाread
readBuiltin आप एक चर जिनमें से आप नाम, एक तथ्य है जो यहाँ-तार के साथ संयोजन के रूप में इस्तेमाल किया जा सकता देने के लिए मान निर्दिष्ट कर सकते हैं:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
IFSहिस्सा है और विकल्प-r लगता है कि मूल्य के रूप में-है असाइन किया गया है, जबकि विकल्प बनाने के -d ''बहु लाइन मान निर्दिष्ट कर सकते हैं। इस अंतिम विकल्प के कारण, कमांड एक गैर-शून्य निकास कोड के साथ लौटता है।
ध्यान दें, क्योंकि हम यहाँ एक स्ट्रिंग का उपयोग कर रहे हैं, एक नया वर्ण मान में जोड़ा जाता है।
downsides:
- कुछ अस्पष्ट है;
- एक गैर-शून्य निकास कोड के साथ रिटर्न;
- मान के लिए एक नई रेखा जोड़ता है।
1c। साथ सौंपनाprintf
बाश 3.1 (2005 जारी) के बाद से, printfबिल्डिन अपना परिणाम एक चर को भी दे सकता है जिसका नाम दिया गया है। पिछले समाधानों के विपरीत, यह सिर्फ काम करता है, चीजों को भागने, विभाजन को रोकने और इतने पर अतिरिक्त प्रयास की आवश्यकता नहीं है।
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
downsides:
- कम पोर्टेबल (लेकिन, अच्छी तरह से)।
विधि 2. एक "संदर्भ" चर का उपयोग करना
बैश 4.3 (2014 में रिलीज़) के बाद से, declareबिल्डिन के पास -nएक वैरिएबल बनाने के लिए एक विकल्प होता है , जो कि दूसरे वेरिएबल के लिए "नाम संदर्भ" होता है, बहुत कुछ जैसे C ++ संदर्भ। जैसे विधि 1 में, संदर्भ अलियास किए गए चर के नाम को संग्रहीत करता है, लेकिन हर बार संदर्भ एक्सेस किया जाता है (या तो पढ़ने या असाइन करने के लिए), बैश स्वचालित रूप से अप्रत्यक्ष हल करता है।
इसके अलावा, बैश के पास संदर्भ के मूल्य को प्राप्त करने के लिए एक विशेष और बहुत ही भ्रामक वाक्यविन्यास है, अपने आप से न्यायाधीश ${!ref}:।
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
यह नीचे बताए गए नुकसान से नहीं बचता है, लेकिन कम से कम यह वाक्यविन्यास को सीधा बनाता है।
downsides:
जोखिम
इन सभी अलियासिंग तकनीकों में कई जोखिम हैं। हर बार जब आप अप्रत्यक्ष को हल करते हैं (या तो पढ़ने या असाइन करने के लिए) तो पहले वाला मनमाना कोड निष्पादित कर रहा है । वास्तव में, एक स्केलर वैरिएबल नाम के बजाय, जैसे var_37, आप एलीस एरेस सबस्क्रिप्ट, जैसे हो सकते हैं arr[42]। लेकिन बैश हर बार जरूरत पड़ने पर वर्ग कोष्ठक की सामग्री का मूल्यांकन करता है, इसलिए एलियासिंग arr[$(do_evil)]पर अप्रत्याशित प्रभाव पड़ेगा ... परिणामस्वरूप, इन तकनीकों का उपयोग केवल तब करें जब आप उपनाम की सिद्धता को नियंत्रित करते हैं ।
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
दूसरा जोखिम चक्रीय उर्फ पैदा कर रहा है। जैसा कि बैश चर उनके नाम से पहचाने जाते हैं और उनके दायरे से नहीं, आप अनजाने में खुद के लिए एक उपनाम बना सकते हैं (यह सोचते हुए कि यह एक घेरने के दायरे से एक चर को उर्फ कर देगा)। यह विशेष रूप से सामान्य चर नाम (जैसे var) का उपयोग करते समय हो सकता है । परिणामस्वरूप, इन तकनीकों का उपयोग केवल तब करें जब आप उपनाम वाले चर के नाम को नियंत्रित करते हैं ।
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
स्रोत: