Eval का उपयोग करके bash में वैरिएबल को स्पेस-वैल्यू कैसे असाइन करें


19

मैं वैरिएबल का उपयोग करके मानों को गतिशील रूप से असाइन करना चाहता हूं eval। निम्नलिखित डमी उदाहरण काम करता है:

var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange

हालाँकि, जब चर मान में रिक्त स्थान होते हैं, तो evalएक त्रुटि देता है, भले ही $var_valueदोहरे उद्धरण चिह्नों के बीच रखा जाए:

var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found

इसे दरकिनार करने का कोई तरीका?

जवाबों:



11

इसके evalलिए उपयोग न करें ; उपयोग करें declare

var_name="fruit"
var_value="blue orange"
declare "$var_name=$var_value"

ध्यान दें कि शब्द-विभाजन एक मुद्दा नहीं है, क्योंकि निम्नलिखित सब कुछ =को मान लिया जाता है declare, न कि पहला शब्द।

में bash4.3, नामित संदर्भ यह एक छोटे से सरल बनाने।

$ declare -n var_name=fruit
$ var_name="blue orange"
$ echo $fruit
blue orange

आप काम कर सकते हैं eval, लेकिन आपको अभी भी नहीं होना चाहिए :) का उपयोग करना evalएक बुरी आदत है।

$ eval "$(printf "%q=%q" "$var_name" "$var_value")"

2
eval उस तरीके का इस्तेमाल करना गलत है। आप $var_valueइसे पास करने से पहले विस्तार कर रहे हैं evalजिसका अर्थ है कि इसे शेल कोड के रूप में व्याख्या किया जाएगा! (उदाहरण के लिए प्रयास करें var_value="';:(){ :|:&};:'")
स्टीफन चेज़लस

1
अच्छी बात; कुछ ऐसे तार हैं जिनका आप सुरक्षित रूप से उपयोग नहीं कर सकते हैं eval(जो एक कारण है कि मैंने कहा कि आपको उपयोग नहीं करना चाहिए eval)।
शेपनर

@chepner - मुझे विश्वास नहीं है कि यह सच है। शायद यह है, लेकिन कम से कम यह एक नहीं है। पैरामीटर प्रतिस्थापन सशर्त विस्तार की अनुमति देते हैं, और इसलिए आप ज्यादातर मामलों में केवल सुरक्षित मूल्यों का विस्तार कर सकते हैं, मुझे लगता है। फिर भी, आपकी प्राथमिक समस्या $var_valueउद्धरण के उलट में से एक है - के लिए एक सुरक्षित मान $var_name (जो कि खतरनाक धारणा के रूप में हो सकती है, वास्तव में) , तो आपको सिंगल-कोट्स के भीतर राइट-हैंड-साइड के दोहरे उद्धरणों को संलग्न करना चाहिए - नहीं विपरीतता से।
मोकेसर सेप 12'14

मुझे लगता है कि मैंने इसका evalउपयोग करके printfऔर इसके bashविशिष्ट %qप्रारूप को ठीक कर लिया है । यह अभी भी उपयोग करने के लिए एक सिफारिश नहीं है eval, लेकिन मुझे लगता है कि यह पहले की तुलना में अधिक सुरक्षित है। यह तथ्य कि आपको इसे काम करने के लिए इस प्रयास में जाने की आवश्यकता है, इस बात का प्रमाण है कि आपको declareइसके बजाय संदर्भों का उपयोग या नाम होना चाहिए ।
शेपनर

खैर, वास्तव में, मेरी राय में, नामित संदर्भ समस्या हैं। इसका उपयोग करने का सबसे अच्छा तरीका है - मेरे अनुभव में - ऐसा है ... set -- a bunch of args; eval "process2 $(process1 "$@")"जहां process1सिर्फ प्रिंट की तरह संख्याएं उद्धृत की जाती हैं "${1}" "${8}" "${138}"। यह पागल सरल है - और '"${'$((i=$i+1))'}" 'ज्यादातर मामलों में जितना आसान है । अनुक्रमित संदर्भ इसे सुरक्षित, मजबूत और तेज़ बनाते हैं । फिर भी - मैंने उत्थान किया।
मोकेसर सेप

4

साथ काम करने का एक अच्छा तरीका evalयह है कि इसे echoपरीक्षण के लिए बदल दिया जाए । echoऔर evalउसी तरह काम करें (यदि हम \xकुछ echoकार्यान्वयनों द्वारा किए गए विस्तार bashको कुछ शर्तों के तहत अलग कर दें)।

दोनों आदेशों के बीच में एक स्थान के साथ उनके तर्क शामिल होते हैं। अंतर यह है कि है echo को प्रदर्शित करता है परिणाम, जबकि eval मूल्यांकन करता है / व्याख्या खोल कोड के रूप में परिणाम।

तो, देखना है कि शेल कोड क्या है

eval $(echo $var_name=$var_value)

मूल्यांकन करेंगे, आप चला सकते हैं:

$ echo $(echo $var_name=$var_value)
fruit=blue orange

यह वह नहीं है जो आप चाहते हैं, जो आप चाहते हैं:

fruit=$var_value

इसके अलावा, $(echo ...)यहाँ उपयोग करने का कोई मतलब नहीं है।

उपरोक्त उत्पादन करने के लिए, आप दौड़ेंगे:

$ echo "$var_name=\$var_value"
fruit=$var_value

तो, इसकी व्याख्या करने के लिए, यह बस है:

eval "$var_name=\$var_value"

ध्यान दें कि इसका उपयोग व्यक्तिगत सरणी तत्वों को सेट करने के लिए भी किया जा सकता है:

var_name='myarray[23]'
var_value='something'
eval "$var_name=\$var_value"

जैसा कि दूसरों ने कहा है, यदि आपको अपने कोड के bashविशिष्ट होने की परवाह नहीं है , तो आप निम्न का उपयोग कर सकते हैं declare:

declare "$var_name=$var_value"

हालाँकि ध्यान दें कि इसके कुछ दुष्प्रभाव हैं।

यह चर के दायरे को उस फ़ंक्शन तक सीमित करता है जहां यह चलाया जाता है। इसलिए आप इसका उपयोग चीजों में उदाहरण के लिए नहीं कर सकते हैं:

setvar() {
  var_name=$1 var_value=$2
  declare "$var_name=$var_value"
}
setvar foo bar

क्योंकि यह घोषित करने के लिए एक fooचर स्थानीय setvarबेकार होगा।

bash-4.2एक वैश्विक चर घोषित -gकरने के लिए एक विकल्प जोड़ा गया है , लेकिन वह नहीं है जो हम चाहते हैं कि या तो हमारे रूप में एक वैश्विक संस्करण सेट हो जाए क्योंकि कॉलर एक फ़ंक्शन के विपरीत था, जैसे:declaresetvar

setvar() {
  var_name=$1 var_value=$2
  declare -g "$var_name=$var_value"
}
foo() {
  local myvar
  setvar myvar 'some value'
  echo "1: $myvar"
}
foo
echo "2: $myvar"

जो उत्पादन होगा:

1:
2: some value

यह भी ध्यान दें कि जब declareकहा जाता है declare(वास्तव bashमें कोर्न शेल के typesetबिलिन से अवधारणा को उधार लिया गया है ), यदि चर पहले से ही सेट है, declareतो एक नया चर घोषित नहीं करता है और जिस तरह से असाइनमेंट किया जाता है वह चर के प्रकार पर निर्भर करता है।

उदाहरण के लिए:

varname=foo
varvalue='([PATH=1000]=something)'
declare "$varname=$varvalue"

यदि varnameएक स्केलर , सरणी या साहचर्य सरणी के रूप में घोषित किया गया था, तो एक अलग परिणाम (और संभावित रूप से बुरा दुष्प्रभाव होगा) का उत्पादन करेगा ।


2
बैश विशिष्ट होने में क्या गलत है? ओपी ने बैश टैग को सवाल पर लगाया, इसलिए वह बैश का उपयोग कर रहा है। विकल्प प्रदान करना अच्छा है, लेकिन मुझे लगता है कि किसी को शेल की एक विशेषता का उपयोग न करने के लिए कहना क्योंकि यह पोर्टेबल नहीं है मूर्खतापूर्ण है।
पैट्रिक

@ पैट्रिक, स्माइली देखी? यह कहने के बाद कि, पोर्टेबल सिंटैक्स का उपयोग करने का अर्थ है कम प्रयास जब आपको अपने कोड को किसी अन्य सिस्टम पर पोर्ट करने की आवश्यकता होती है जहां bashउपलब्ध नहीं है (या जब आपको पता चलता है कि आपको एक बेहतर / तेज शेल की आवश्यकता है)। evalसभी बॉर्न की तरह गोले में वाक्य रचना काम करता है और POSIX है, इसलिए सभी प्रणालियों एक होगा shजहां कि काम करता है। (इसका मतलब यह भी है कि मेरा उत्तर सभी गोले पर लागू होता है, और जल्द ही या बाद में, जैसा कि अक्सर यहां होता है, आप एक गैर-बैश विशिष्ट प्रश्न को इस के डुप्लिकेट के रूप में बंद देखेंगे।
स्टीफन चेज़लस

लेकिन क्या होगा अगर $var_nameइसमें टोकन हों? ... जैसे ;?
मोकेसर सेप

@ बाइक, तो यह एक चर नाम नहीं है। आप अपनी सामग्री भरोसा नहीं कर सकते हैं, तो आप दोनों के साथ यह साफ़ करने में की जरूरत है evalऔर declare(के बारे में सोच PATH, TMOUT, PS4, SECONDS...)।
स्टीफन चेजलस

लेकिन पहले पर इसका हमेशा एक चर विस्तार और दूसरे तक चर नाम कभी नहीं होता है। मेरे जवाब में मैं इसे एक पैरामीटर विस्तार के साथ पवित्रा करता हूं, लेकिन अगर आप पहले पास पर एक उप-प्रकार में स्वच्छता का काम कर रहे हैं, तो यह भी हो सकता है कि पोर्ट डब्ल्यू / export। हालांकि मैं अंत में पैरेंटिकल बिट का पालन नहीं करता हूं।
मोकेसर सेप 12'14

1

यदि तुम करो:

eval "$name=\$val"

... और $nameइसमें एक ;या कई अन्य टोकन हैं जो शेल को एक सरल कमांड के रूप में व्याख्या कर सकते हैं - उचित शेल सिंटैक्स द्वारा पूर्ववर्ती, जिसे निष्पादित किया जाएगा।

name='echo hi;varname' val='be careful with eval'
eval "$name=\$val" && echo "$varname"

आउटपुट

hi
be careful with eval

हालांकि कभी-कभी ऐसे बयानों के मूल्यांकन और निष्पादन को अलग करना संभव हो सकता है। उदाहरण के लिए, aliasकमांड का पूर्व-मूल्यांकन करने के लिए उपयोग किया जा सकता है। निम्नलिखित उदाहरण में चर परिभाषा को सहेजा गया है, aliasजिसे केवल तभी सफलतापूर्वक घोषित किया जा सकता है यदि $nmचर का मूल्यांकन करने वाले में कोई बाइट्स न हों जो ASCII अल्फ़ान्यूमेरिक्स से मेल नहीं खाते या _

LC_OLD=$LC_ALL LC_ALL=C
alias "${nm##*[!_A-Z0-9a-z]*}=_$nm=\$val" &&
eval "${nm##[0-9]*}" && unalias "$nm"
LC_ALL=$LC_OLD

evalaliasएक varname से नया आह्वान करने के लिए यहां उपयोग किया जाता है । लेकिन यह केवल तभी कहा जाता है जब पिछली aliasपरिभाषा सफल होती है, और जब मैं जानता हूं कि बहुत सारे अलग-अलग कार्यान्वयन aliasनाम के लिए बहुत सारे विभिन्न प्रकार के मूल्यों को स्वीकार करेंगे , मैं अभी तक एक में नहीं चला हूं जो पूरी तरह से एक को स्वीकार करेगा ।

भीतर परिभाषा aliasके लिए है _$nm, तथापि, और यह सुनिश्चित करना है कि कोई महत्वपूर्ण पर्यावरण मूल्यों पर लिखा जाता है। मैं किसी भी उल्लेखनीय पर्यावरण मूल्यों के बारे में नहीं जानता हूं, जिसकी शुरुआत _आम तौर पर होती है और यह आमतौर पर अर्ध-निजी घोषणा के लिए एक सुरक्षित शर्त है।

वैसे भी, अगर aliasपरिभाषा सफल होती है , तो यह एक aliasनाम घोषित करेगा $nm। और evalकेवल यह कहेंगे कि aliasयदि संख्या के साथ भी शुरू नहीं होता है - तो evalकेवल एक अशक्त तर्क मिलता है। इसलिए यदि दोनों शर्तों को पूरा किया जाता evalहै, तो उपनाम को कॉल किया जाता है और उपनाम में सहेजी गई चर परिभाषा बनाई जाती है, जिसके बाद नए aliasको तुरंत हैश तालिका से हटा दिया जाता है।


;चर नामों में अनुमति नहीं है। यदि आपके पास सामग्री पर नियंत्रण नहीं है $name, तो आपको इसे export/ declareसाथ के लिए साफ करने की आवश्यकता है । हालांकि export, कोड को निष्पादित नहीं करता है, जैसे कुछ चर सेट करना PATH, PS4और उनमें से कई info -f bash -n 'Bash Variables'समान रूप से खतरनाक दुष्प्रभाव हैं।
स्टीफन चेज़लस

@ स्टीफनचेज़लस - बेशक, इसकी अनुमति नहीं है, लेकिन, पहले की तरह, इसका evalपहला नाम पास नहीं है - यह एक चर विस्तार है। जैसा कि आपने कहीं और कहा है, उस संदर्भ में इसकी बहुत अनुमति है। फिर भी $ PATH का तर्क बहुत अच्छा है - मैंने एक छोटा सा संपादन किया और कुछ बाद में जोड़ूंगा।
mikeserv

@ स्टीफनचेज़लस - देर से बेहतर कभी नहीं ...?
15

अभ्यास में, zsh, pdksh, mksh, yashपर शिकायत नहीं है unset 'a;b'
स्टीफन चेज़लस

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