eval
और exec
दोनों को bash (1) के कमांड में बनाया गया है जो कमांड निष्पादित करते हैं।
मैं यह भी देखता हूं exec
कि कुछ विकल्प हैं लेकिन क्या केवल यही अंतर है? उनके संदर्भ में क्या होता है?
eval
और exec
दोनों को bash (1) के कमांड में बनाया गया है जो कमांड निष्पादित करते हैं।
मैं यह भी देखता हूं exec
कि कुछ विकल्प हैं लेकिन क्या केवल यही अंतर है? उनके संदर्भ में क्या होता है?
जवाबों:
eval
और exec
पूरी तरह से अलग जानवर हैं। (इस तथ्य के अलावा कि दोनों कमांड चलाएंगे, लेकिन ऐसा सब कुछ आप एक शेल में करते हैं।)
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
क्या exec cmd
करता है, बस चलने के समान है cmd
, सिवाय इसके कि वर्तमान शेल को कमांड के साथ प्रतिस्थापित किया जाता है, बजाय एक अलग प्रक्रिया के चलाया जा रहा है। आंतरिक रूप से, कहते हैं कि एक बच्चे की प्रक्रिया बनाने के लिए /bin/ls
कॉल fork()
किया जाएगा , और फिर exec()
बच्चे को निष्पादित करने के लिए /bin/ls
। exec /bin/ls
दूसरी ओर कांटा नहीं होगा , लेकिन सिर्फ खोल को बदल देगा।
की तुलना करें:
$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo
साथ में
$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217
echo $$
मेरे द्वारा शुरू किए गए शेल के पीआईडी को प्रिंट करता है, और लिस्टिंग /proc/self
से हमें उस शेल का पीआईडी मिलता है जो शेल ls
से चलाया गया था। आमतौर पर, प्रक्रिया आईडी अलग होते हैं, लेकिन exec
शेल के साथ और ls
समान प्रक्रिया आईडी होती है। इसके exec
बाद, शेल के बदले जाने के बाद कमांड का पालन नहीं हुआ।
दूसरी ओर:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
eval
वर्तमान शेल में कमांड के रूप में तर्क चलाएगा। दूसरे शब्दों eval foo bar
में बस के रूप में ही है foo bar
। लेकिन चर को निष्पादित करने से पहले विस्तारित किया जाएगा, इसलिए हम शेल चर में सहेजे गए कमांड निष्पादित कर सकते हैं:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
यह एक बच्चे की प्रक्रिया नहीं बनाएगा, इसलिए चर को वर्तमान शेल में सेट किया गया है। (बेशक eval /bin/ls
एक बच्चे की प्रक्रिया पैदा करेगा, उसी तरह एक सादा पुराना /bin/ls
होगा।)
या हमारे पास एक कमांड हो सकती है जो शेल कमांड को आउटपुट करती है। रनिंग ssh-agent
पृष्ठभूमि में एजेंट को शुरू करता है, और चर असाइनमेंट का एक गुच्छा आउटपुट करता है, जिसे वर्तमान शेल में सेट किया जा सकता है और इसका उपयोग चाइल्ड प्रोसेस ( ssh
कमांड्स जो आप चलाएंगे) द्वारा किया जाता है । इसलिए ssh-agent
इसके साथ शुरू किया जा सकता है:
eval $(ssh-agent)
और वर्तमान शेल को विरासत में अन्य कमांड के लिए वैरिएबल मिलेंगे।
बेशक, अगर वैरिएबल cmd
में कुछ होने जैसा है rm -rf $HOME
, तो रनिंग eval "$cmd"
कुछ ऐसा नहीं होगा जिसे आप करना चाहते हैं। यहां तक कि स्ट्रिंग के अंदर कमांड प्रतिस्थापन जैसी चीजों को संसाधित किया जाएगा, इसलिए किसी को वास्तव में यह सुनिश्चित करना चाहिए कि इनपुट का eval
उपयोग करने से पहले सुरक्षित है।
अक्सर, eval
गलती से कोड और डेटा को गलती से भी मिश्रण से बचने और बचने के लिए संभव है ।
eval
बारे में सामान्य अस्वीकरण जोड़ने की याद दिलाता है। परोक्ष रूप से संशोधित चर की तरह सामग्री के माध्यम से कई गोले में किया जा सकता है declare
/ typeset
/ nameref
और जैसे विस्तार ${!var}
, इसलिए मैं बजाय उन का प्रयोग करेंगे eval
जब तक कि मैं वास्तव में इसे से बचने के लिए किया था।
exec
एक नई प्रक्रिया नहीं बनाता है। यह नई कमांड के साथ वर्तमान प्रक्रिया को बदल देता है। यदि आपने कमांड लाइन पर ऐसा किया है, तो यह प्रभावी रूप से आपके शेल सत्र को समाप्त कर देगा (और शायद आपको लॉग आउट करें या टर्मिनल विंडो बंद करें!)
जैसे
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
यहाँ मैं ksh
(मेरा सामान्य खोल) हूँ। मैं शुरू करता हूं bash
और फिर बैश I के अंदर exec /bin/echo
। हम देख सकते हैं कि मुझे ksh
बाद में वापस छोड़ दिया गया है क्योंकि bash
प्रक्रिया द्वारा प्रतिस्थापित किया गया था /bin/echo
।
exec
यदि कोई आदेश निर्दिष्ट नहीं किया गया है, तो नई और हैंडल स्ट्रीम पुनर्निर्देशन / फ़ाइल डिस्क्रिप्टर के साथ वर्तमान शेल प्रक्रिया को बदलने के लिए उपयोग किया जाता है। eval
कमांड के रूप में स्ट्रिंग्स का मूल्यांकन करने के लिए उपयोग किया जाता है। दोनों का उपयोग रन-टाइम में ज्ञात तर्कों के साथ एक कमांड को बनाने और निष्पादित करने के लिए किया जा सकता है, लेकिन exec
कमांड निष्पादित करने के अलावा वर्तमान शेल की प्रक्रिया को बदल देता है।
वाक्य - विन्यास:
exec [-cl] [-a name] [command [arguments]]
नियमावली के अनुसार यदि इस अंतर्निहित में निर्दिष्ट कमांड है
... खोल की जगह। कोई नई प्रक्रिया नहीं बनाई जाती है। तर्क आज्ञा देने के तर्क बन जाते हैं।
दूसरे शब्दों में, यदि आप bash
PID 1234 के साथ चल रहे थे और यदि आप exec top -u root
उस शेल के भीतर चल रहे थे , तो top
कमांड में PID 1234 होगा और आपकी शेल प्रक्रिया को बदल देगा।
यह कहां उपयोगी है? आवरण लिपियों के रूप में जानी जाने वाली किसी चीज़ में। इस तरह की स्क्रिप्ट तर्कों के सेट का निर्माण करती हैं या कुछ निर्णय exec
लेती हैं कि कौन से चर पर्यावरण में पारित होते हैं, और फिर जो कुछ भी निर्दिष्ट किया गया है, उसके साथ खुद को बदलने के लिए उपयोग करते हैं, और निश्चित रूप से उन्हीं तर्कों को प्रदान करते हैं जो आवरण स्क्रिप्ट ने रास्ते में बनाए हैं।
मैनुअल यह भी बताता है कि:
यदि कमांड निर्दिष्ट नहीं है, तो कोई भी पुनर्निर्देशन वर्तमान शेल में प्रभावी होता है
यह हमें एक फ़ाइल में वर्तमान गोले आउटपुट धाराओं से कुछ भी पुनर्निर्देशित करने की अनुमति देता है। यह लॉगिंग या फ़िल्टरिंग उद्देश्यों के लिए उपयोगी हो सकता है, जहाँ आप stdout
केवल कमांड नहीं देखना चाहते हैं stderr
। उदाहरण के लिए, जैसे:
bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt
2017年 05月 20日 星期六 05:01:51 MDT
HELLO WORLD
यह व्यवहार शेल स्क्रिप्ट में लॉगिंग के लिए काम करता है , अलग-अलग फ़ाइलों या प्रक्रियाओं के लिए धाराओं को पुनर्निर्देशित करता है , और फ़ाइल डिस्क्रिप्टर के साथ अन्य मज़ेदार सामान ।
कम से कम bash
संस्करण 4.3 के लिए स्रोत कोड स्तर पर , exec
अंतर्निहित को परिभाषित किया गया है builtins/exec.def
। यह प्राप्त आदेशों को पार्स करता है, और यदि कोई हो, तो यह फ़ाइल shell_execve()
में परिभाषित फ़ंक्शन पर चीजों को पास करता है execute_cmd.c
।
लंबी कहानी छोटी, exec
सी प्रोग्रामिंग भाषा में आदेशों का एक परिवार मौजूद है , और shell_execve()
मूल रूप से एक आवरण समारोह है execve
:
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
बाश 4.3 मैनुअल स्टेट्स (मेरे द्वारा जोड़ा गया जोर):
आर्गल्स को एक ही कमांड में पढ़ा और समेटा जाता है। इस आदेश को तब शेल द्वारा पढ़ा और निष्पादित किया जाता है , और इसके निकास की स्थिति को निष्कासन के मूल्य के रूप में वापस किया जाता है।
ध्यान दें कि कोई भी प्रक्रिया प्रतिस्थापन नहीं होती है। इसके विपरीत exec
जहां लक्ष्य execve()
कार्यक्षमता का अनुकरण करना है, eval
बिल्ट केवल तर्कों का "मूल्यांकन" करने के लिए कार्य करता है, जैसे कि उपयोगकर्ता ने उन्हें कमांड लाइन पर टाइप किया है। जैसे, नई प्रक्रियाएँ बनती हैं।
यह कहाँ उपयोगी हो सकता है? जैसा कि गाइल्स ने इस उत्तर में बताया , "... eval का उपयोग अक्सर नहीं किया जाता है। कुछ गोले में, सबसे आम उपयोग एक चर के मूल्य को प्राप्त करना है जिसका नाम रनटाइम तक ज्ञात नहीं है"। व्यक्तिगत रूप से, मैंने इसका उपयोग उबंटू में कुछ लिपियों में किया है जहाँ उपयोगकर्ता द्वारा वर्तमान में उपयोग किए जा रहे विशिष्ट कार्यक्षेत्र के आधार पर एक कमांड को निष्पादित / मूल्यांकन करना आवश्यक था।
स्रोत कोड स्तर पर, यह कार्य builtins/eval.def
करने के लिए पार्स किए गए इनपुट स्ट्रिंग को अंदर और पास evalstring()
करता है।
अन्य बातों के अलावा, eval
वे चर पेश कर सकते हैं जो वर्तमान शेल निष्पादन वातावरण में बने रहते हैं, जबकि exec
वे नहीं कर सकते:
$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
एक नई बाल प्रक्रिया बनाना, तर्क चलाना और निकास की स्थिति वापस करना।
ओह क्या? इसका संपूर्ण बिंदु eval
यह है कि यह किसी भी तरह से एक बच्चे की प्रक्रिया नहीं बनाता है। यदि मैं करता हूँ
eval "cd /tmp"
एक शेल में, फिर बाद में वर्तमान शेल ने निर्देशिका बदल दी होगी। न तो exec
एक नई बाल प्रक्रिया बनाता है, इसके बजाय यह दिए गए एक के लिए वर्तमान निष्पादन योग्य (अर्थात् शेल) को बदलता है; प्रक्रिया आईडी (और खुली फ़ाइलें और अन्य सामान) एक ही रहते हैं। विरोध के रूप में eval
, एक exec
कॉलिंग शेल में वापस नहीं आएगा जब तक exec
कि निष्पादन योग्य को खोजने या लोड करने में असमर्थ होने के कारण या विफलता के विस्तार की समस्याओं को हल करने में असमर्थ होने के कारण स्वयं विफल हो जाता है।
eval
मूल रूप से अपने तर्क (ओं) को एक स्ट्रिंग के रूप में संघनन के बाद व्याख्या करता है, अर्थात् यह वाइल्डकार्ड विस्तार और तर्क विभाजन की एक अतिरिक्त परत करेगा। exec
ऐसा कुछ नहीं करता।
मूल्यांकन
ये काम:
$ echo hi
hi
$ eval "echo hi"
hi
$ exec echo hi
hi
हालाँकि, ये नहीं हैं:
$ exec "echo hi"
bash: exec: echo hi: not found
$ "echo hi"
bash: echo hi: command not found
प्रक्रिया छवि प्रतिस्थापन
यह उदाहरण दर्शाता है कि exec
इसकी कॉलिंग प्रक्रिया की छवि कैसे बदल जाती है:
# Get PID of current shell
sh$ echo $$
1234
# Enter a subshell with PID 5678
sh$ sh
# Check PID of subshell
sh-subshell$ echo $$
5678
# Run exec
sh-subshell$ exec echo $$
5678
# We are back in our original shell!
sh$ echo $$
1234
सूचना जो exec echo $$
उप-समूह के PID के साथ चलती है! इसके अलावा, यह पूरा होने के बाद, हम अपने मूल sh$
शेल में वापस आ गए थे ।
दूसरी ओर, प्रक्रिया छवि को प्रतिस्थापित नहींeval
करता है । बल्कि, यह दी गई कमांड को चलाता है जैसा कि आप सामान्य रूप से शेल के भीतर ही करेंगे। (बेशक, अगर आप एक कमांड चलाते हैं जिसे स्पॉन करने के लिए एक प्रक्रिया की आवश्यकता होती है ... यह बस यही करता है!)
sh$ echo $$
1234
sh$ sh
sh-subshell$ echo $$
5678
sh-subshell$ eval echo $$
5678
# We are still in the subshell!
sh-subshell$ echo $$
5678
exec
)