बाश और इसके विशिष्ट उपयोगों में eval कमांड


165

बैश मैन पेज पढ़ने के बाद और इस पोस्ट के संबंध में ।

मुझे अभी भी यह समझने में परेशानी हो रही है कि वास्तव में evalकमांड क्या करता है और इसके विशिष्ट उपयोग क्या होंगे। उदाहरण के लिए अगर हम करते हैं:

bash$ set -- one two three  # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

वास्तव में यहां क्या हो रहा है और डॉलर के संकेत और बैकस्लैश समस्या में कैसे बंधते हैं?


1
रिकॉर्ड के लिए, दूसरा प्रयास काम करता है। एक उपधारा में $($n)चलता $nहै। यह कमांड चलाने की कोशिश करता है 1जो मौजूद नहीं है।
मार्टिन विकमैन

1
@MartinWickman लेकिन आवश्यकता echo $1अंततः चलाने की है , नहीं 1। मुझे नहीं लगता कि यह सबशेल्स का उपयोग करके किया जा सकता है।
हरि मेनन

5
आपको उपयोगeval करने के सुरक्षा निहितार्थ के बारे में पता होना चाहिए ।
अगली सूचना तक रोक दिया गया।

1
@ Raze2dust: मुझे विश्वास नहीं है कि वह सुझाव दे रहे थे कि इसे उपधाराओं के साथ चलाया जा सकता है, बल्कि यह बताते हुए कि ओपी सूचीबद्ध 5 वीं कमान क्यों काम नहीं करती।
जेडवर्ड

जवाबों:


196

evalएक स्ट्रिंग को उसके तर्क के रूप में लेता है, और इसका मूल्यांकन करता है जैसे कि आपने कमांड लाइन पर उस स्ट्रिंग को टाइप किया है। (यदि आप कई तर्क पास करते हैं, तो वे पहले उनके बीच रिक्त स्थान के साथ शामिल हो जाते हैं।)

${$n}बैश में एक वाक्यविन्यास त्रुटि है। ब्रेसिज़ के अंदर, आप केवल कुछ संभावित उपसर्ग और प्रत्ययों के साथ एक चर नाम रख सकते हैं, लेकिन आपके पास मनमाने ढंग से वाक्यविन्यास नहीं हो सकता है और विशेष रूप से आप चर विस्तार का उपयोग नहीं कर सकते हैं। कहने का एक तरीका है "चर का नाम जिसका नाम इस चर में है", हालांकि:

echo ${!n}
one

$(…)कोष्ठक के अंदर निर्दिष्ट कमांड चलाता है (जो एक अलग प्रक्रिया में है जो सभी सेटिंग्स जैसे कि मौजूदा शेल से परिवर्तनशील मान प्राप्त करता है), और इसके आउटपुट को इकट्ठा करता है। तो एक शेल कमांड के रूप में echo $($n)चलता है $n, और इसके आउटपुट को प्रदर्शित करता है। चूंकि $nमूल्यांकन करने के लिए 1, $($n)कमांड को चलाने का प्रयास 1करता है, जो मौजूद नहीं है।

eval echo \${$n}को पारित मापदंडों चलाता है eval। विस्तार के बाद, पैरामीटर हैं echoऔर ${1}। इसलिए eval echo \${$n}कमांड चलाता है echo ${1}

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

$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *

evalबहुत बार उपयोग नहीं किया जाता है। कुछ गोले में, सबसे आम उपयोग एक चर के मूल्य को प्राप्त करना है जिसका नाम रनटाइम तक ज्ञात नहीं है। बैश में, ${!VAR}वाक्यविन्यास के लिए यह आवश्यक नहीं है । evalअभी भी उपयोगी है जब आपको ऑपरेटर, आरक्षित शब्द, आदि युक्त एक लंबी कमांड बनाने की आवश्यकता होती है।


ऊपर मेरी टिप्पणी के संबंध में, कितने "पास" से निष्कासन होता है?

@ Konos5 क्या टिप्पणी है? evalएक स्ट्रिंग प्राप्त करता है (जो स्वयं पार्स और मूल्यांकन का परिणाम हो सकता है), और इसे कोड स्निपेट के रूप में व्याख्या करता है।
गिल्स एसओ- बुराई को रोकना '

Raze2dust के उत्तर के तहत मैंने एक टिप्पणी छोड़ दी है। अब मुझे विश्वास है कि eval ज्यादातर dereferencing प्रयोजनों के लिए उपयोग किया जाता है। यदि मैं eval echo \ $ {$ n} टाइप करता हूं तो मुझे एक मिलता है। हालाँकि अगर मैं echo \ $ {$ n} टाइप करता हूँ तो मुझे \ _ {1} मिलता है। मेरा मानना ​​है कि यह eval के "टू-पास" पार्सिंग के कारण हो रहा है। मैं अब सोच रहा हूं कि अगर मुझे अतिरिक्त i = n घोषणा का उपयोग करते हुए तिगुनी आवश्यकता हो तो क्या होगा। इस मामले में Raze2dust के अनुसार मुझे बस एक अतिरिक्त निकासी की आवश्यकता है। मेरा मानना ​​है कि हालांकि एक बेहतर तरीका होना चाहिए ... (यह आसानी से बरबाद हो सकता है)
kstratis

@ Konos5 मैं उपयोग नहीं करेगा eval eval। मुझे याद है कभी जरूरत महसूस नहीं हो सकती। यदि आपको वास्तव में दो evalपास की आवश्यकता है, तो एक अस्थायी चर का उपयोग करें, यह डीबग करना आसान होगा eval tmp="\${$i}"; eval x="\${$tmp}":।
गिल्स एसओ- बुराई को रोकना '

1
@ Konos5 "दो बार पार्स" थोड़ा भ्रामक है। कुछ लोगों को यह विश्वास करने के लिए नेतृत्व किया जा सकता है कि बैश में शाब्दिक स्ट्रिंग तर्क को निर्दिष्ट करने की कठिनाई के कारण जो विभिन्न विस्तार से सुरक्षित है। evalबस एक स्ट्रिंग में कोड लेता है और सामान्य नियमों के अनुसार इसका मूल्यांकन करता है। तकनीकी रूप से, यह सही भी नहीं है, क्योंकि कुछ कोने के मामले हैं जिनमें बैश ने पार्सिंग को संशोधित करने के लिए भी निष्कासन के तर्कों का प्रदर्शन नहीं किया है - लेकिन यह बहुत ही अस्पष्ट टीडबिट है जिसमें मुझे संदेह है कि किसी को भी पता नहीं है।
ओरमज

39

केवल निष्कासन के बारे में सोचें "निष्पादन से पहले आपकी अभिव्यक्ति का एक अतिरिक्त समय का मूल्यांकन"

eval echo \${$n}echo $1मूल्यांकन के पहले दौर के बाद बन जाता है । नोटिस में तीन बदलाव:

  • \$बन गया $(बैकस्लैश की जरूरत है, अन्यथा यह मूल्यांकन करने के लिए कोशिश करता है ${$n}, नाम के एक चर जिसका अर्थ है {$n}, जिसकी अनुमति नहीं है)
  • $n के लिए मूल्यांकन किया गया था 1
  • evalगायब हो गया

दूसरे दौर में, यह मूल रूप से है echo $1जिसे सीधे निष्पादित किया जा सकता है।

तो eval <some command>पहले मूल्यांकन करेंगे <some command>(यहां मूल्यांकन से मेरा मतलब है कि स्थानापन्न चर, सही पात्रों के साथ बच गए पात्रों को प्रतिस्थापित करें आदि), और फिर परिणामी अभिव्यक्ति को एक बार फिर से चलाएं।

evalका उपयोग तब किया जाता है जब आप गतिशील रूप से चर बनाना चाहते हैं, या विशेष रूप से इस तरह पढ़ने के लिए डिज़ाइन किए गए कार्यक्रमों से आउटपुट पढ़ने के लिए। उदाहरण के लिए http://mywiki.wooledge.org/BashFAQ/048 देखें । लिंक में कुछ विशिष्ट तरीके भी शामिल हैं जिनका evalउपयोग किया जाता है, और इसके साथ जुड़े जोखिम।


3
पहली गोली के लिए एक नोट के रूप में, ${VAR}वाक्यविन्यास की अनुमति है, और कोई अस्पष्टता होने पर पसंद किया जाता है (करता है $VAR == $V, उसके बाद ARया $VAR == $VAउसके बाद R)। ${VAR}के बराबर है $VAR। वास्तव में, इसके चर नाम की $nअनुमति नहीं है।
जेडवर्ड

2
eval eval echo \\\${\${$i}}एक ट्रिपल डीफेरेंस करेंगे। मुझे यकीन नहीं है कि ऐसा करने का एक सरल तरीका है। इसके अलावा, मेरी मशीन पर \${$n}ठीक काम करता है (प्रिंट one) ..
हरी मेनन

2
@ कोनोस 5 echo \\\${\${$i}}प्रिंट \${${n}}। $ {1} के eval echo \\\${\${$i}}बराबर है eval eval echo \\\ $ {\ $ {$ i}} `` और प्रिंट के बराबर है । echo \${${n}}`` and prints . eval echo ${1}one
गिल्स एसओ- बुराई को रोकना '

2
@ Konos5 उसी तर्ज पर सोचें - पहला ` escapes the second one, and the third `उसके $बाद बच जाता है। तो यह \${${n}}मूल्यांकन के एक दौर के बाद बन जाता है
हरि मेनन

2
@ कोनोस 5 लेफ्ट-टू-राइट बोली और बैकलैश पार्सिंग के लिए सोचने का सही तरीका है। पहले \\ एक बैकस्लैश उपज। फिर \$एक डॉलर की उपज। और इसी तरह।
गिल्स एसओ- बुराई को रोकें '

25

मेरे अनुभव में, eval का एक "विशिष्ट" उपयोग कमांड चलाने के लिए है जो पर्यावरण चर को सेट करने के लिए शेल कमांड उत्पन्न करता है।

शायद आपके पास एक प्रणाली है जो पर्यावरण चर का एक संग्रह का उपयोग करती है, और आपके पास एक स्क्रिप्ट या प्रोग्राम है जो निर्धारित करता है कि किन लोगों को सेट किया जाना चाहिए और उनके मूल्य। जब भी आप कोई स्क्रिप्ट या प्रोग्राम चलाते हैं, तो यह एक कांटे की प्रक्रिया में चलता है, इसलिए जब भी यह बाहर निकलता है, तो यह पर्यावरण चर से सीधे कुछ भी करता है। लेकिन वह स्क्रिप्ट या प्रोग्राम निर्यात कमांड को stdout में भेज सकता है।

Eval के बिना, आपको stdout को temp फ़ाइल में बदलना होगा, temp फ़ाइल को स्रोत करना होगा और फिर उसे हटाना होगा। Eval के साथ, आप बस कर सकते हैं:

eval "$(script-or-program)"

ध्यान दें कि उद्धरण महत्वपूर्ण हैं। इसे ले लीजिए (उदाहरण के लिए):

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!

आम साधनों का कोई उदाहरण है जो ऐसा करता है? टूल में शेल कमांड का एक सेट बनाने का एक साधन है जिसे खाली करने के लिए पारित किया जा सकता है?
जोकिम

@ जोकिम मुझे किसी भी ओपनसोर्स टूल के बारे में नहीं पता जो इसे करते हैं, लेकिन इसका इस्तेमाल कुछ निजी स्क्रिप्ट में उन कंपनियों में किया जाता है जहां मैंने काम किया है। मैंने अभी इस तकनीक का फिर से xampp के साथ उपयोग करना शुरू कर दिया है। Apache .conf फाइलें लिखी गई पर्यावरण चर का विस्तार करती हैं ${varname}। मुझे पर्यावरण चर द्वारा मानकीकृत कुछ चीजों के साथ कई अलग-अलग सर्वरों पर समान .conf फ़ाइलों का उपयोग करना सुविधाजनक लगता है। मैंने एडिट / ऑप्ट / लैम्प / xampp (जो अपाचे शुरू करता है) को एक स्क्रिप्ट के साथ इस तरह का इकलौता करने के लिए किया है जो सिस्टम के चारों ओर पॉक्स करता है और export.conf फाइलों के लिए वैरिएबल को परिभाषित करने के लिए स्टेटमेंट बैश करता है।
21

@Joakim वैकल्पिक के लिए एक स्क्रिप्ट होगी जो प्रभावित हो रही प्रत्येक .conf फ़ाइलों को एक टेम्पलेट से, चारों ओर एक ही पोकिंग के आधार पर उत्पन्न करती है। एक बात जो मुझे अपने रास्ते से बेहतर लगती है, वह यह है कि अपाचे को शुरू करना / ऑप्ट / लैम्प / xampp से गुजरे बिना बासी आउटपुट स्क्रिप्ट का उपयोग नहीं करता है, बल्कि यह शुरू करने में विफल रहता है क्योंकि पर्यावरण चर कुछ भी नहीं और अवैध निर्देशों का विस्तार करते हैं।
22

@Anthony Sottile मैं आपको $ (स्क्रिप्ट-या-प्रोग्राम) के आसपास उद्धरण जोड़ने का उत्तर संपादित करते हुए देख रहा हूं, यह कहते हुए कि वे कई कमांड चलाते समय महत्वपूर्ण थे। क्या आप एक उदाहरण प्रदान कर सकते हैं - निम्नलिखित foo.sh के स्टैडआउट में अर्ध-बृहदान्त्र के अलग किए गए कमांड के साथ ठीक काम करता है: इको '' /! / Bin / bash '> foo.sh; गूंज 'गूंज "गूंज -na; गूंज -nb; गूंज -n c"' >> foo.sh; chmod 755 foo.sh; eval $ (./ foo.sh)। यह stdout पर abc पैदा करता है। चल रहा है। गूंज -nb; गूंज -nc
सूतक

1
एक सामान्य उपकरण के उदाहरण के लिए जो eval का उपयोग करता है, pyenv देखें । पाइनेव आपको पायथन के कई संस्करणों के बीच आसानी से स्विच करने देता है। आप eval "$(pyenv init -)"अपने .bash_profile(या समान) शेल कॉन्फ़िगरेशन फ़ाइल में डालते हैं । यह थोड़ा शेल स्क्रिप्ट का निर्माण करता है और फिर वर्तमान शेल में इसका मूल्यांकन करता है।
जैरी101

10

Eval statement शेल को eval के तर्कों को कमांड के रूप में लेने और कमांड-लाइन के माध्यम से चलाने के लिए कहता है। यह नीचे जैसी स्थिति में उपयोगी है:

आपकी स्क्रिप्ट में यदि आप एक कमांड को एक चर में परिभाषित कर रहे हैं और बाद में आप उस कमांड का उपयोग करना चाहते हैं तो आपको eval का उपयोग करना चाहिए:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >

4

अद्यतन: कुछ लोगों का कहना है कि किसी को भी- eval का उपयोग करना चाहिए। मैं असहमत हूं। मुझे लगता है कि जोखिम तब होता है जब भ्रष्ट इनपुट को पारित किया जा सकता है eval। हालाँकि ऐसी कई सामान्य स्थितियाँ हैं जहाँ यह जोखिम नहीं है, और इसलिए यह जानने योग्य है कि किसी भी स्थिति में इसका उपयोग कैसे किया जाए। यह स्टैकओवरफ़्लो उत्तर स्पष्ट करने के विकल्प और eval के विकल्प बताता है। अंततः यह निर्धारित करने के लिए उपयोगकर्ता पर निर्भर है कि क्या / जब eval सुरक्षित है और उपयोग करने के लिए कुशल है।


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

शायद सबसे सीधा उदाहरण एक बैश प्रोग्राम होगा जो टेक्स्ट फाइल के रूप में एक और बैश स्क्रिप्ट खोलता है, टेक्स्ट की प्रत्येक पंक्ति को पढ़ता है, और evalउन्हें क्रम में निष्पादित करने के लिए उपयोग करता है। यह अनिवार्य रूप से बैश sourceस्टेटमेंट के समान व्यवहार है , जो कि एक का उपयोग करेगा, जब तक कि आयातित स्क्रिप्ट की सामग्री पर किसी प्रकार का परिवर्तन (जैसे फ़िल्टरिंग या प्रतिस्थापन) करना आवश्यक नहीं था।

मुझे शायद ही कभी आवश्यकता होती है eval, लेकिन मैंने उन चर को पढ़ना या लिखना उपयोगी पाया है जिनके नाम अन्य चर को सौंपे गए तार में समाहित थे। उदाहरण के लिए, चर के सेट पर कार्रवाई करना, जबकि कोड पदचिह्न को छोटा रखना और अतिरेक से बचना।

evalवैचारिक रूप से सरल है। हालाँकि, बैश भाषा के सख्त सिंटैक्स, और बैश दुभाषिया के पार्सिंग क्रम को बारीक किया जा सकता है और यह evalक्रिप्टिक और उपयोग करने या समझने में मुश्किल हो सकता है। यहाँ आवश्यक हैं:

  1. पास किया गया तर्क evalएक स्ट्रिंग अभिव्यक्ति है जिसे रनटाइम पर परिकलित किया जाता है। अपनी स्क्रिप्ट में कोड की वास्तविक लाइन के evalरूप में इसके तर्क के अंतिम पार्स किए गए परिणाम को निष्पादित करेगा ।

  2. सिंटेक्स और पार्सिंग क्रम कड़े हैं। यदि परिणाम आपकी स्क्रिप्ट के दायरे में बैश कोड की एक निष्पादन योग्य रेखा नहीं है, तो प्रोग्राम उस evalकथन पर क्रैश हो जाएगा क्योंकि यह कचरा निष्पादित करने का प्रयास करता है।

  3. परीक्षण करते समय आप evalस्टेटमेंट को बदल सकते हैं echoऔर जो प्रदर्शित होता है उसे देख सकते हैं। यदि यह वर्तमान संदर्भ में वैध कोड है, तो इसे चलाने से evalकाम चल जाएगा।


निम्नलिखित उदाहरण स्पष्ट करने में मदद कर सकते हैं कि कैसे काम करता है ...

उदाहरण 1:

eval 'सामान्य' कोड के सामने बयान एक एनओपी है

$ eval a=b
$ eval echo $a
b

उपरोक्त उदाहरण में, पहले evalबयानों का कोई उद्देश्य नहीं है और इसे समाप्त किया जा सकता है। evalपहली पंक्ति में व्यर्थ है क्योंकि कोड के लिए कोई गतिशील पहलू नहीं है, अर्थात यह पहले से ही bash कोड की अंतिम पंक्तियों में पार्स हो गया है, इस प्रकार यह bash स्क्रिप्ट में कोड के एक सामान्य कथन के समान होगा। 2 evalभी व्यर्थ है, क्योंकि, हालांकि एक $aशाब्दिक चरण है जो इसके शाब्दिक स्ट्रिंग समतुल्य में परिवर्तित होता है, कोई अप्रत्यक्षता नहीं है (उदाहरण के लिए वास्तविक बैश संज्ञा या बैश-आयोजित स्क्रिप्ट चर के स्ट्रिंग मान के माध्यम से कोई संदर्भ नहीं है ), इसलिए यह पहचान का व्यवहार करेगा evalउपसर्ग के बिना कोड की एक पंक्ति के रूप में ।



उदाहरण 2:

स्ट्रिंग मानों के रूप में दिए गए var नामों का उपयोग करके var असाइनमेंट निष्पादित करें।

$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval

यदि आप थे echo $key=$val, तो आउटपुट होगा:

mykey=myval

यह , स्ट्रिंग पार्सिंग का अंतिम परिणाम है, जो कि निष्कासन द्वारा निष्पादित किया जाएगा, इसलिए अंत में गूंज कथन का परिणाम ...



उदाहरण 3:

उदाहरण 2 में अधिक अप्रत्यक्ष जोड़ना

$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing

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

 eval eval \$$keyA=\$$valA  # substitution of $keyA and $valA by interpreter
 eval eval \$keyB=\$valB    # convert '$' + name-strings to real vars by eval
 eval $keyB=$valB           # substitution of $keyB and $valB by interpreter
 eval that=amazing          # execute string literal 'that=amazing' by eval

यदि मान लिया गया कि पार्सिंग आदेश यह स्पष्ट नहीं करता है कि eval पर्याप्त क्या कर रहा है, तो तीसरा उदाहरण अधिक स्पष्ट रूप से पार्सिंग का वर्णन करने में मदद कर सकता है कि क्या चल रहा है।



उदाहरण 4:

डिस्कवर करें कि क्या var, जिनके नाम तार में समाहित हैं, स्वयं में स्ट्रिंग मान हैं।

a="User-provided"
b="Another user-provided optional value"
c=""

myvarname_a="a"
myvarname_b="b"
myvarname_c="c"

for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
    eval varval=\$$varname
    if [ -z "$varval" ]; then
        read -p "$varname? " $varname
    fi
done

पहली यात्रा में:

varname="myvarname_a"

बैश तर्क को पार्स करता है eval, और evalरनटाइम में इसे सचमुच देखता है:

eval varval=\$$myvarname_a

निम्नलिखित छद्मकोड यह दर्शाने का प्रयास करता है कि कैसे वास्तविक कोड की उपरोक्त रेखा की व्याख्या की जाती है , जिसके द्वारा निष्पादित अंतिम मूल्य पर पहुंचने के लिए eval। (निम्नलिखित लाइनें वर्णनात्मक हैं, सटीक बैश कोड नहीं):

1. eval varval="\$" + "$varname"      # This substitution resolved in eval statement
2. .................. "$myvarname_a"  # $myvarname_a previously resolved by for-loop
3. .................. "a"             # ... to this value
4. eval "varval=$a"                   # This requires one more parsing step
5. eval varval="User-provided"        # Final result of parsing (eval executes this)

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

varval="User-provided"

उपर्युक्त उदाहरण में शेष कोड यह देखने के लिए परीक्षण करता है कि क्या $ varval को दिया गया मान शून्य है, और यदि हां, तो उपयोगकर्ता को एक मूल्य प्रदान करने के लिए संकेत देता है।


3

मैं मूल रूप से जानबूझकर कभी नहीं सीख पाया कि कैसे इवल का उपयोग किया जाए, क्योंकि ज्यादातर लोग प्लेग की तरह इससे दूर रहने की सलाह देंगे। हालाँकि, मैंने हाल ही में एक ऐसे उपयोग के मामले की खोज की, जिसने मुझे इसे जल्द पहचान न पाने के लिए चेहरा बना दिया।

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

कहते हैं कि आपके पास सामग्री के साथ /etc/cron.d/repeatme पर क्रॉन जॉब है:

*/10 * * * * root program arg1 arg2

आप इसके सामने सभी कबाड़ के साथ एक स्क्रिप्ट के रूप में निष्पादित कर सकते हैं, लेकिन हम सभी कबाड़ से छुटकारा पाने के लिए कटौती का उपयोग कर सकते हैं, इसे एक उपधारा में लपेट सकते हैं, और स्ट्रिंग को निष्कासित कर सकते हैं।

eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)

कट कमांड केवल फ़ाइल के 6 वें क्षेत्र को प्रिंट करता है, रिक्त स्थान द्वारा सीमांकित। Eval तब उस कमांड को निष्पादित करता है।

मैंने एक उदाहरण के रूप में यहां एक क्रॉन जॉब का इस्तेमाल किया, लेकिन अवधारणा स्टैडआउट से पाठ को प्रारूपित करना है, और फिर उस पाठ का मूल्यांकन करना है।

इस मामले में निष्कासन का उपयोग असुरक्षित नहीं है, क्योंकि हम वास्तव में जानते हैं कि हम हाथ से पहले क्या मूल्यांकन करेंगे।


2

मुझे हाल ही में evalकई ब्रेस एक्सपेंसेस को मजबूर करने के लिए उपयोग करना पड़ा है ताकि मुझे उस ऑर्डर का मूल्यांकन किया जा सके। बैश बाएं से दाएं कई ब्रेस एक्सपेंशन करता है, इसलिए

xargs -I_ cat _/{11..15}/{8..5}.jpg

तक फैलता है

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

लेकिन मुझे दूसरे ब्रेस विस्तार की आवश्यकता थी, जो पहले किया गया था, उपज

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

सबसे अच्छा मैं ऐसा करने के लिए आ सकता था

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

यह काम करता है क्योंकि एकल उद्धरण evalकमांड लाइन के पार्सिंग के दौरान विस्तार से ब्रेसिज़ के पहले सेट की रक्षा करते हैं , जिससे उन्हें उप-आह्वान द्वारा विस्तारित किया जा सकता है eval

नेस्टेड ब्रेस विस्तार से जुड़ी कुछ चालाक योजना हो सकती है जो इसे एक चरण में होने देती है, लेकिन अगर मैं बहुत पुराना और इसे देखने के लिए बेवकूफ हूं।


1

आपने सामान्य उपयोगों के बारे में पूछा।

शेल स्क्रिप्टिंग के बारे में एक आम शिकायत यह है कि आप (कथित रूप से) फ़ंक्शन से वापस मान प्राप्त करने के लिए संदर्भ द्वारा पारित नहीं कर सकते हैं।

लेकिन वास्तव में, "eval" के माध्यम से, आप संदर्भ से गुजर सकते हैं। कॉलली द्वारा मूल्यांकन किए जाने वाले चर असाइनमेंट की सूची को कैली वापस पारित कर सकता है। यह संदर्भ द्वारा पारित किया जाता है क्योंकि कॉलर परिणाम चर (ओं) के नाम (ओं) को निर्दिष्ट करने की अनुमति दे सकता है - नीचे उदाहरण देखें। त्रुटिपूर्ण परिणाम त्रुटिपूर्ण और त्रुटिपूर्ण जैसे मानक नाम दिए जा सकते हैं।

यहाँ एक उदाहरण को bash में पास करने का उदाहरण दिया गया है:

#!/bin/bash
isint()
{
    re='^[-]?[0-9]+$'
    [[ $1 =~ $re ]]
}

#args 1: name of result variable, 2: first addend, 3: second addend 
iadd()
{
    if isint ${2} && isint ${3} ; then
        echo "$1=$((${2}+${3}));errno=0"
        return 0
    else
        echo "errstr=\"Error: non-integer argument to iadd $*\" ; errno=329"
        return 1
    fi
}

var=1
echo "[1] var=$var"

eval $(iadd var A B)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi
echo "[2] var=$var (unchanged after error)"

eval $(iadd var $var 1)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi  
echo "[3] var=$var (successfully changed)"

आउटपुट इस तरह दिखता है:

[1] var=1
errstr=Error: non-integer argument to iadd var A B
errno=329
[2] var=1 (unchanged after error)
[3] var=2 (successfully changed)

उस टेक्स्ट आउटपुट में लगभग असीमित बैंड चौड़ाई है! और कई संभावनाएं हैं यदि कई आउटपुट लाइनों का उपयोग किया जाता है: उदाहरण के लिए, पहली पंक्ति का उपयोग चर असाइनमेंट के लिए किया जा सकता है, दूसरा निरंतर 'विचार की धारा' के लिए, लेकिन यह इस पद के दायरे से परे है।


यह कहना कि "अधिक संभावनाएं हैं" कम से कम कहने के लिए ट्रिफ़ल, तुच्छ और निरर्थक हैं।
डॉट

0

मुझे जवाब देने से पहले "आपकी अभिव्यक्ति का मूल्यांकन करने से पहले एक अतिरिक्त बार" पसंद है, और दूसरे उदाहरण के साथ स्पष्ट करना चाहते हैं।

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

विकल्प 2 में जिज्ञासु परिणाम यह है कि हमने 2 पैरामीटर दिए हैं:

  • पहला पैरामीटर: "value
  • दूसरा पैरामीटर: content"

काउंटर सहज के लिए कैसे है? जो अतिरिक्त evalतय करेगा।

Https://stackoverflow.com/a/40646371/744133 से अनुकूलित


0

प्रश्न में:

who | grep $(tty | sed s:/dev/::)

आउटपुट त्रुटियां यह दावा करती हैं कि फाइलें और टैटी मौजूद नहीं हैं। मैंने इसका मतलब यह समझा कि tre को grep के निष्पादन से पहले व्याख्यायित नहीं किया जा रहा है, बल्कि इसके बजाय bash ने tty को grep के पैरामीटर के रूप में पारित किया, जिसने इसे फ़ाइल नाम के रूप में व्याख्या की।

नेस्टेड पुनर्निर्देशन की स्थिति भी है, जिसे मिलान किए गए कोष्ठक द्वारा नियंत्रित किया जाना चाहिए, जो एक बच्चे की प्रक्रिया को निर्दिष्ट करना चाहिए, लेकिन बैश मुख्य रूप से एक शब्द विभाजक है, जिससे प्रोग्राम में भेजे जाने के लिए पैरामीटर बनाया जाता है, इसलिए पहले कोष्ठक का मिलान नहीं किया जाता है, लेकिन इसकी व्याख्या की जाती है देखा।

मुझे grep के साथ विशिष्ट मिला, और एक पाइप का उपयोग करने के बजाय फ़ाइल को एक पैरामीटर के रूप में निर्दिष्ट किया। मैंने बेस कमांड को भी सरल बनाया, एक कमांड से आउटपुट को फाइल के रूप में पास किया, ताकि i / o पाइपिंग को नेस्टेड न किया जाए:

grep $(tty | sed s:/dev/::) <(who)

अच्छा काम करता है।

who | grep $(echo pts/3)

वास्तव में वांछित नहीं है, लेकिन नेस्टेड पाइप को समाप्त करता है और अच्छी तरह से काम करता है।

अंत में, बैश नेस्टेड पिपिंग को पसंद नहीं करता है। यह समझना महत्वपूर्ण है कि बैश एक पुनरावर्ती तरीके से लिखा गया नया-तरंग कार्यक्रम नहीं है। इसके बजाय, बैश एक पुराना 1,2,3 कार्यक्रम है, जिसे सुविधाओं के साथ जोड़ा गया है। पिछड़ी संगतता सुनिश्चित करने के प्रयोजनों के लिए, व्याख्या के प्रारंभिक तरीके को कभी भी संशोधित नहीं किया गया है। यदि बैश को पहले कोष्ठकों से मिलान करने के लिए फिर से लिखा गया, तो कितने बैश कार्यक्रमों में कितने बग पेश किए जाएंगे? कई प्रोग्रामर को क्रिप्टोकरंसी पसंद है।

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