2012-10-08 47 views
5

हर कोई कहता है कि eval बुरा है, और आपको प्रतिस्थापन के रूप में $() का उपयोग करना चाहिए। लेकिन मैंने ऐसी स्थिति में भाग लिया है जहां unquoting $() के अंदर समान नहीं है।

पृष्ठभूमि यह है कि मुझे फ़ाइल पथों द्वारा अक्सर उन जगहों पर जला दिया गया है, और ऐसे सभी मार्गों को उद्धृत करना पसंद है। यह जानना चाहते हैं कि मेरे सभी एक्जिक्यूटिव कहां से आ रहे हैं। और भी भयावह, खुद पर भरोसा नहीं करते, और इसलिए बनाए गए आदेशों को प्रदर्शित करने में सक्षम होने की तरह मैं दौड़ने वाला हूं।

मैं नीचे बनाम $ (eval का उपयोग) में बदलाव की कोशिश, और आदेश नाम उद्धृत किया गया है कि क्या (यह cuz रिक्तियाँ सकता है)

BIN_LS="/bin/ls" 
    thefile="arf" 
    thecmd="\"${BIN_LS}\" -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '"/bin/ls" -ld -- "arf"' 
     ./foo.sh: line 8: "/bin/ls": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '"/bin/ls" -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    thecmd="${BIN_LS} -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access "arf": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    $("/bin/ls" -ld -- "${thefile}") 

     /bin/ls: cannot access arf: No such file or directory 

तो ... यह भ्रामक है। $() निर्माण के अलावा एक उद्धृत कमांड पथ हर जगह मान्य है? एक छोटे, अधिक प्रत्यक्ष उदाहरण:

$ c="\"/bin/ls\" arf" 
$ $($c) 
-bash: "/bin/ls": No such file or directory 
$ eval $c 
/bin/ls: cannot access arf: No such file or directory 
$ $("/bin/ls" arf) 
/bin/ls: cannot access arf: No such file or directory 
$ "/bin/ls" arf 
/bin/ls: cannot access arf: No such file or directory 

कैसे एक साधारण $($c) मामले की व्याख्या करता है?

उत्तर

3

इस तथ्य के साथ कि यह पहली जगह में समझ में नहीं आता है। Use an array instead.

$ c=("/bin/ls" arf) 
$ "${c[@]}" 
/bin/ls: cannot access arf: No such file or directory 
+0

अनगिनत तारों का तुरंत मूल्यांकन किया जाता है। सी = ("/ बिन/एलएस"/बिन/एल *) वांछित से काफी अलग है। (echo "'$ {c [@]}'") के साथ देखें) c = ("/ bin/ls" "/ bin/l *") तो z = $ ("$ {c [@]}") विफल रहता है। तर्कों की एक सरणी का उपयोग $() के साथ अच्छी तरह से नहीं किया जा सकता है? – Shenme

+1

हालांकि, सी = ("/ बिन/एलएस" "/ बिन/एल *") तो z = $ ($ {c [@]}) ठीक काम करता प्रतीत होता है। क्या यह निष्पादन योग्य रूप $() है जो आपको दिखाने के लिए था? – Shenme

5

" के उपयोग के शब्द उद्धृत करने के लिए बैश के साथ अपनी सहभागिता का हिस्सा है। आप प्रॉम्प्ट पर

$ "/bin/ls" arf 

टाइप करें, या एक स्क्रिप्ट में, आप बैश कह रहे हैं कि आदेश वास्तव में बल कर रहे हैं /bin/ls एक शब्द है कि शब्द /bin/ls और arf, और डबल उद्धरण के होते है।

आप टाइप

$ eval '"/bin/ls" arf' 

आप बैश कह रहे हैं कि आदेश शब्द eval और "/bin/ls" arf के होते है। चूंकि eval के प्रयोजन के नाटक करने के लिए है कि इसके तर्क एक वास्तविक मानव-इनपुट आदेश है, इस

$ "/bin/ls" arf 

चल के बराबर है और " सिर्फ प्रॉम्प्ट पर की तरह संसाधित हो जाता है।

ध्यान दें कि यह प्रस्तुति eval के लिए विशिष्ट है; बैश आमतौर पर नाटक करने के अपने रास्ते से बाहर नहीं जाता है कि कुछ वास्तविक मानव-टाइप किया गया आदेश था।

जब आप टाइप

$ c='"/bin/ls" arf' 
$ $c 

$c प्रतिस्थापित हो जाता है, और फिर शब्द बंटवारे (§3.5.7 "Word Splitting" in the Bash Reference Manual देखें) से होकर गुजरती है, तो आदेश के शब्दों "/bin/ls" (ध्यान दें डबल उद्धरण!) और arf हैं। कहने की जरूरत नहीं है, यह काम नहीं करता है। (यह भी बहुत सुरक्षित नहीं है, क्योंकि शब्द-विभाजन के अलावा, $c भी फाइलनाम-विस्तार और क्या नहीं है। आम तौर पर आपका पैरामीटर-विस्तार हमेशा डबल-कोट्स में होना चाहिए, और यदि वे नहीं हो सकते हैं, तो आपको अपना पुनः लिखना चाहिए कोड ताकि वे हो सकें। अनगिनत पैरामीटर-विस्तार परेशानी के लिए पूछ रहे हैं।)

जब आप टाइप

$ c='"/bin/ls" arf' 
$ $($c) 

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

इग्नेसियो वेज़क्वेज़-अब्राम उसके जवाब में कहते हैं, सही समाधान एक सरणी का उपयोग करें, और संभाल करने के लिए ठीक से हवाले से है:

$ c=("/bin/ls" arf) 
$ "${c[@]}" 

जो दो तत्वों, /bin/ls और arf साथ एक सरणी के लिए c सेट, और उन दो तत्वों को कमांड के शब्द के रूप में उपयोग करता है।

+0

(5 मिनट की सीमा से नफरत है) यही कारण है कि अंतिम पंक्ति एक रूप जहां उत्पादन पर कब्जा कर लिया जा सकता है (जो हालांकि मूल प्रश्न में कहा नहीं की जरूरत है) में एक पैक आदेश के निष्पादन नहीं दिखा रहा है c = ("/ bin/ls" प्रयास करें "/ बिन/एल * ") और सी = ("/bin/ls "/ bin/l *) echo" $ {c [@]} 'के साथ "। यह विधि मुझे बाद में उपयोग ($) के साथ उद्धृत तर्कों के साथ एक सरणी बनाने की अनुमति नहीं देगी ...? – Shenme

+1

@ शेंमे: जहां तक ​​मैं कह सकता हूं, आप क्या कह रहे हैं, "मैं' eval' का उपयोग करना चाहता हूं, क्योंकि मैं बैश कमांड के रूप में एक मनमानी स्ट्रिंग को संसाधित करना चाहता हूं। " हर कोई क्या कह रहा है, "eval' बुरा है, क्योंकि यह बैश कमांड के रूप में एक मनमानी स्ट्रिंग को संसाधित करता है।"आप इन दृष्टिकोणों को सुलझाने की कोशिश कर रहे हैं," क्या यह बुराई करने के लिए कुछ * गैर-बेवकूफ तरीका है? ", लेकिन यह काम नहीं करेगा। दृष्टिकोण मूल रूप से असहनीय हैं। एक बुरा काम करने के लिए, उपयोग करें एक बुरा उपकरण; या, बुराई उपकरण से बचने के लिए, बुराई करने से बचने के लिए अपनी स्क्रिप्ट को फिर से लिखें। – ruakh

+0

नहीं, कोई मनमाना आदेश नहीं, कोई बुरा इरादा नहीं: .) बस एक आदेश बनाना और इसे नियंत्रित तरीके से निष्पादित करना चाहता था मैं इनपुट को नियंत्रित करूंगा, और '-' का उपयोग करने के लिए भी भयावह हूं। लोग जटिल मेटा-प्रश्नों के सरल जवाब देखते हैं, और अगर आप eval का उपयोग करते हैं तो आप की आलोचना करते हैं, इसलिए मैं आलोचना से बचना चाहता था।: - (वैसे भी, z = $ ($ {c [@]}) ठीक काम करता है। – Shenme

2

man page for bash से, eval के बारे में:

eval [आर्ग ...]: आर्ग पढ़ सकते हैं और एक भी आदेश में एक साथ concatenated रहे हैं। यह आदेश तब खोल द्वारा पढ़ा और निष्पादित किया जाता है, और इसके बाहर निकलने स्थिति eval के मान के रूप में वापस कर दी जाती है।

c"\"/bin/ls\" arf" के रूप में परिभाषित किया जाता है, बाहरी उद्धरण का कारण होगा पूरे बातeval को पहला तर्क है, जो एक आदेश या कार्यक्रम होने की उम्मीद है के रूप में संसाधित करने के लिए। आपको अपने eval तर्कों को इस तरह से पास करने की आवश्यकता है कि लक्ष्य कमांड और उसके तर्क अलग-अलग सूचीबद्ध हैं।

$(...) निर्माण eval से अलग व्यवहार करता है क्योंकि यह कोई आदेश नहीं है जो तर्क लेता है। यह एक समय में तर्कों को संसाधित करने के बजाय एक बार में पूरे आदेश को संसाधित कर सकता है।

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

+0

अतिरिक्त युक्ति: जब यह देखने का प्रयास किया जाता है कि शैल द्वारा विस्तारित आदेश कैसे देखा जाता है, तो 'set -v' का उपयोग करके कभी-कभी' echo' से अधिक सटीक परिणाम मिल सकते हैं। – bta

+0

इसलिए मूल रूप से, 'eval' सुरक्षित है यदि मूल्यांकन स्ट्रिंग उपयोगकर्ता इनपुट के किसी भी तरीके से प्रकट नहीं होती है; लेकिन अगर हम इसे रोकने पर किसी भी समय विफल हो जाते हैं, तो इसका शोषण किया जा सकता है; इसलिए '' ... '' या '$ (...) 'का उपयोग करके' eval' की तुलना में कम सावधानी बरतनी चाहिए; थक्स आदमी! यह वह विशिष्ट युक्ति थी जिसे मैं अपनी स्क्रिप्ट बदलने से पहले ढूंढ रहा था! :) –