कमांड के रूप में चर; eval बनाम बैश -c


41

मैं किसी व्यक्ति द्वारा बनाई गई बैश स्क्रिप्ट पढ़ रहा था और मैंने देखा कि लेखक एक कमांड के रूप में एक वेरिएबल का मूल्यांकन करने के लिए eval का उपयोग नहीं करता
है

bash -c "$1"

के बजाय

eval "$1"

मुझे लगता है कि eval का उपयोग करना पसंदीदा तरीका है और यह शायद वैसे भी तेज है। क्या यह सच है?
क्या दोनों के बीच कोई व्यावहारिक अंतर है? दोनों के बीच उल्लेखनीय अंतर क्या हैं?


कुछ अवसरों में, आप या तो बिना दूर हो सकते हैं। e='echo foo'; $eठीक काम करता है।
डेनिस

जवाबों:


40

eval "$1"वर्तमान स्क्रिप्ट में कमांड निष्पादित करता है। यह वर्तमान स्क्रिप्ट से शेल चर सेट और उपयोग कर सकता है, वर्तमान स्क्रिप्ट के लिए पर्यावरण चर सेट कर सकता है, वर्तमान स्क्रिप्ट से फ़ंक्शन सेट और उपयोग कर सकता है, वर्तमान निर्देशिका के लिए वर्तमान निर्देशिका, umask, सीमाएं और अन्य विशेषताओं को सेट कर सकता है, और इसी तरह। bash -c "$1"एक पूरी तरह से अलग स्क्रिप्ट में कमांड निष्पादित करता है, जो पर्यावरण चर, फ़ाइल डिस्क्रिप्टर और अन्य प्रक्रिया पर्यावरण (लेकिन किसी भी परिवर्तन को वापस प्रेषित नहीं करता है) पर वार करता है, लेकिन आंतरिक शेल सेटिंग्स (शेल चर, फ़ंक्शन, विकल्प, जाल, आदि) को विरासत में नहीं मिलता है।

एक और तरीका है, (eval "$1")जो एक सब-कमांड में कमांड निष्पादित करता है: यह कॉलिंग स्क्रिप्ट से सब कुछ विरासत में मिला है, लेकिन किसी भी परिवर्तन को वापस प्रेषित नहीं करता है।

उदाहरण के लिए, यह सोचते हैं कि चर dirनिर्यात नहीं किया जाता है और $1है cd "$foo"; lsतो,:

  • cd /starting/directory; foo=/somewhere/else; eval "$1"; pwd/somewhere/elseऔर प्रिंट की सामग्री को सूचीबद्ध करता है /somewhere/else
  • cd /starting/directory; foo=/somewhere/else; (eval "$1"); pwd/somewhere/elseऔर प्रिंट की सामग्री को सूचीबद्ध करता है /starting/directory
  • cd /starting/directory; foo=/somewhere/else; bash -c "$1"; pwdकी सामग्री को सूचीबद्ध करता है /starting/directory(क्योंकि cd ""वर्तमान निर्देशिका को नहीं बदलता है) और प्रिंट करता है /starting/directory

धन्यवाद। मैं (eval "$ 1") के बारे में नहीं जानता था, क्या यह स्रोत से अलग है?
हूमी

1
@ भोमी (eval "$1")का कोई लेना देना नहीं है source। यह सिर्फ का एक संयोजन है (…)और evalsource fooके बराबर है eval "$(cat foo)"
गिल्स एसओ- बुराई को रोकना '

हम एक ही समय में अपने उत्तर लिख रहे होंगे ...
mikeserv

@whoami के बीच प्राथमिक अंतर evalऔर .dotवह यह है कि evalसाथ काम करता है तर्क और .dotसाथ काम करता है फ़ाइलें।
mikeserv

आप दोनों को शुक्रिया। मेरी पिछली टिप्पणी अब थोड़े बेवकूफ लगती है कि मैंने इसे फिर से पढ़ा ...
हूमी

23

के बीच सबसे महत्वपूर्ण अंतर

bash -c "$1" 

तथा

eval "$1"

यह है कि पूर्व एक उप में चलता है और बाद में नहीं चलता है। इसलिए:

set -- 'var=something' 
bash -c "$1"
echo "$var"

उत्पादन:

#there doesn't seem to be anything here
set -- 'var=something' 
eval "$1"
echo "$var"

उत्पादन:

something

मुझे इस बात का कोई अंदाजा नहीं है कि कोई भी bashउस तरीके से कभी भी निष्पादन योग्य का उपयोग क्यों करेगा । यदि आपको इसे लागू करना है, तो अंतर्निहित POSIX गारंटी का उपयोग करें sh। या (subshell eval)यदि आप अपने पर्यावरण की रक्षा करना चाहते हैं।

व्यक्तिगत रूप से, मैं शेल के .dotऊपर सभी को पसंद करता हूं ।

printf 'var=something%d ; echo "$var"\n' `seq 1 5` | . /dev/fd/0

आउटपुट

something1
something2
something3
something4
something5

लेकिन क्या आपको यह सब चाहिए?

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

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

var='echo this is var' ; $var

उत्पादन:

this is var

यह काम करता है, लेकिन केवल इसलिए echoकि इसके तर्क गिनती के बारे में परवाह नहीं है।

var='echo "this is var"' ; $var

उत्पादन:

"this is var"

देख? डबल-कोट्स साथ आते हैं क्योंकि शेल के विस्तार के परिणाम का $varमूल्यांकन नहीं किया जाता है quote-removal

var='printf %s\\n "this is var"' ; $var

उत्पादन:

"this
is
var"

लेकिन साथ evalया sh:

    var='echo "this is var"' ; eval "$var" ; sh -c "$var"

उत्पादन:

this is var
this is var

जब हम उपयोग करते हैं evalया shशेल विस्तार के परिणामों पर एक दूसरा पास लेता है और उनका मूल्यांकन एक संभावित कमांड के रूप में भी करता है, और इसलिए उद्धरण एक अंतर बनाते हैं। आप भी कर सकते हैं:

. <<VAR /dev/fd/0
    ${var:=echo "this is var"}
#END
VAR

आउटपुट

this is var

5

मैंने एक त्वरित परीक्षण किया:

time bash -c 'for i in {1..10000}; do bash -c "/bin/echo hi"; done'
time bash -c 'for i in {1..10000}; eval "/bin/echo hi"; done'

(हां, मुझे पता है, मैंने पाश को निष्पादित करने के लिए बैश -c का उपयोग किया था लेकिन इससे कोई फर्क नहीं पड़ना चाहिए)।

परिणाम:

eval    : 1.17s
bash -c : 7.15s

तो evalतेज है। के मैन पेज से eval:

Eval यूटिलिटी एक साथ एक चरित्र के साथ अलग-अलग तर्क प्रस्तुत करके एक कमांड का निर्माण करेगी। निर्मित कमांड को शेल द्वारा पढ़ा और निष्पादित किया जाएगा।

bash -cबेशक, एक बैश शेल में कमांड निष्पादित करता है। एक नोट: मैंने उपयोग किया है /bin/echoक्योंकि echoएक शेल निर्मित है bash, जिसका अर्थ है कि एक नई प्रक्रिया शुरू करने की आवश्यकता नहीं है। जगह /bin/echoके साथ echoके लिए bash -cपरीक्षण, ले लिया 1.28s। उसी के बारे में है। होवेवर, evalनिष्पादनयोग्य चलाने के लिए तेज़ है। यहां मुख्य अंतर यह है कि evalएक नया शेल शुरू नहीं होता है (यह वर्तमान में कमांड को निष्पादित करता है) जबकि bash -cएक नया शेल शुरू होता है, फिर नए शेल में कमांड निष्पादित करता है। एक नया शेल शुरू करने में समय लगता है, और इसीलिए bash -cयह धीमा है eval


मैं ओपी तुलना करने के लिए चाहता है bash -cके साथ evalनहीं exec
जोसेफ आर।

@JosephR। ऊप्स! मैं उसे बदल दूंगा।
प्लाज़्मापावर

1
@JosephR। इसे अब ठीक किया जाना चाहिए। इसके अलावा, मैंने परीक्षणों को थोड़ा और कम किया है और bash -cयह बुरा नहीं है ...
प्लाज़्मापावर 13'14

3
हालांकि यह सच है, यह मूलभूत अंतर को याद करता है कि कमान विभिन्न वातावरणों में निष्पादित की जाती है। यह स्पष्ट है कि बैश का एक नया उदाहरण शुरू करना धीमा होगा, यह एक दिलचस्प अवलोकन नहीं है।
गाइल्स का SO- बुराई होना बंद करो '
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.