एक उद्धृत चर में विकल्प विफल क्यों हो जाते हैं, लेकिन अयोग्य होने पर काम करते हैं?


18

मैंने इस बारे में पढ़ा कि मुझे चारे में चरों को भाग लेना चाहिए, जैसे $ फू के बदले "$ फू"। हालाँकि, एक स्क्रिप्ट लिखते समय, मुझे एक ऐसा मामला मिला, जहाँ यह उद्धरण के बिना काम करता है, लेकिन उनके साथ नहीं:

wget_options='--mirror --no-host-directories'
local_root="$1" # ./testdir recieved from command line
remote_root="$2" # ftp://XXX recieved from command line 
relative_path="$3" # /XXX received from command line

यह एक काम करता है:

wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

यह नहीं होता है (डबल उद्धरण $ wget_options ध्यान दें):

wget "$wget_options" --directory_prefix="$local_root" "$remote_root$relative_path"
  • इसका क्या कारण है?

  • पहली पंक्ति अच्छा संस्करण है; या मुझे यह संदेह होना चाहिए कि कहीं न कहीं एक छिपी हुई त्रुटि है जो इस व्यवहार का कारण बनती है?

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


3
आपके प्रश्न का उत्तर यहां दिया गया है: mywiki.wooledge.org/BashFAQ/050
ग्लेन जैकमैन

3
नियमों के लिए स्रोत पर जाएं: बैश मैनुअल । खंड 3.5 "शेल विस्तार" पर विशेष रूप से ध्यान दें, विशेष रूप से शब्द विभाजन और फ़ाइल नाम विस्तार - ये 2 कारक हैं जिन्हें आप नियंत्रित करने के लिए उद्धरण का उपयोग करते हैं।
ग्लेन जैकमैन


4
मुझे लगता है कि यह समझने में मदद मिलती है कि कमांड लाइन तर्क निम्न स्तर पर कैसे काम करता है। जब किसी कार्यक्रम को निष्पादित किया जाता है, तो यह वर्णों की सूची की सूची के रूप में तर्क प्राप्त करता है (लगभग पर्याप्त)। प्रत्येक आंतरिक सूची जिसे हम "तर्क" कहते हैं। अधिकांश कार्यक्रम args के बीच तार्किक अलगाव पर निर्भर करते हैं। यहाँ, आप देखते हैं कि इसका wgetक्या --mirror --no-host-directoriesअर्थ है (एक तर्क के रूप में), लेकिन यह दो तर्कों में विभाजित होने पर इसे संभालता है । बहुत कम प्रोग्राम विशेष रूप से रिक्त स्थान वेक्टर के अंदर रिक्त स्थान और उद्धरण का इलाज करते हैं। समस्या यह है कि bash, और अन्य गोले,>
HTNW

2
> मनुष्यों द्वारा उपयोग किया जाता है। यह तर्क के बीच की सीमाओं को मैन्युअल रूप से परिभाषित करने के लिए कष्टप्रद होगा, इसलिए गोले एक पंक्ति (वर्णों की सूची) को एक तर्क वेक्टर (वर्णों की सूची की सूची) में बदलने के लिए व्हाट्सएप पर विभाजित हो जाते हैं। परिवर्तनीय विस्तार एक पहला विस्तार bashहै, जिससे आप कल्पना कर सकते हैं कि $aयह सीधे अपनी सामग्री लिखने के बराबर है। अब यह मुद्दा स्पष्ट है: a="-a -b"; cmd "$a"विस्तार करने के लिए cmd "-a -b", लेकिन cmdशायद इसका मतलब यह नहीं है। cmd $aफैलता है cmd -a -b, जो शायद काम करता है
HTNW

जवाबों:


28

मूल रूप से, आपको उन्हें शब्द विभाजन (और फ़ाइल नाम पीढ़ी) से बचाने के लिए चर चर का दोगुना होना चाहिए। हालाँकि, आपके उदाहरण में,

wget_options='--mirror --no-host-directories'
wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

शब्द विभाजन वास्तव में आप क्या चाहते हैं

साथ "$wget_options"(उद्धृत), wgetक्या एक तर्क से कोई लेना देना नहीं जानता है --mirror --no-host-directoriesऔर शिकायत

wget: unknown option -- mirror --no-host-directories

के लिए wgetदो विकल्प देखने के लिए --mirrorऔर --no-host-directoriesके रूप में अलग, शब्द बंटवारे होने के लिये है।

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

अच्छे उत्तरों के साथ संबंधित प्रश्न: व्हॉट्सएप या अन्य विशेष पात्रों पर मेरी शेल स्क्रिप्ट क्यों चोक करती है?


डबल उद्धृत चर विस्तार अंगूठे का एक अच्छा नियम है। वो करो । फिर उन बहुत कम मामलों से अवगत रहें जहाँ आपको ऐसा नहीं करना चाहिए। ये आपको उपर्युक्त त्रुटि संदेश जैसे नैदानिक ​​संदेशों के माध्यम से स्वयं को प्रस्तुत करेंगे।

ऐसे कुछ मामले भी हैं जहाँ आपको चर विस्तार को उद्धृत करने की आवश्यकता नहीं है । लेकिन वैसे भी दोहरे उद्धरण चिह्नों का उपयोग जारी रखना आसान है क्योंकि इससे बहुत अंतर नहीं पड़ता है। ऐसा ही एक मामला है

variable=$other_variable

एक और है

case $variable in
    ...) ... ;;
esac

2
उस विभाजन + ग्लोब ऑपरेटर का उपयोग करने से पहले, किसी को यह सुनिश्चित करने की आवश्यकता हो सकती है कि $IFSइसमें सही मूल्य है। यहां आपको अंतरिक्ष में विभाजित करने की आवश्यकता है और पाठ में कोई टैब या न्यूलाइन शामिल नहीं है, इसलिए डिफ़ॉल्ट मान $IFSहोगा, लेकिन यदि उस कोड को किसी फ़ंक्शन में उपयोग किया जाना है जिसे संदर्भ में कहा जा $IFSसकता है जहां संशोधित किया जा सकता था , आप $IFSपहले से सेट करना चाहते हैं (और संभवतः बाद में इसे पुनर्स्थापित कर सकते हैं या इसके लिए एक स्थानीय गुंजाइश का उपयोग कर सकते हैं यदि बाकी कोड एक $IFS
असमान

32

किसी सरणी का उपयोग करने के लिए कोड का सबसे मजबूत तरीका:

wget_options=(
    --mirror 
    --no-host-directories
    --directory_prefix="$1"
)
wget "${wget_options[@]}" "$2/$3"

यह सही जवाब है। संदर्भ
l0b0

2
यह एक अच्छा जवाब है, इसलिए मैंने इसे उतारा लेकिन कुसालंदा ने मुझे यह समझने में अधिक मदद की कि मेरा कोड गलत क्यों था और मैं केवल एक को ही स्वीकार कर सकता हूं।
z32a7ul

मैं मुसीबत की दुनिया में भाग रहा था जब तक कि rsync सूची में किसी ने मुझे यह निर्माण नहीं दिखाया। यह विशेष रूप से सहायक है यदि कुछ तत्व खाली तार हो सकते हैं। इससे खाली तार गायब हो जाते हैं। यदि आपकी आज्ञा कुछ करने के लिए फैलती है तो कुछ कमांड पसंद करते हैं cpऔर rsyncअप्रत्याशित चीजें करेंगे rsync '' rest of parameters। यह सशर्त रूप से टुकड़ा द्वारा एक कमांड टुकड़े के निर्माण के लिए बहुत अच्छा है और फिर इसे केवल एक बार एक ही स्थान पर चलाना है।
जो

17

आप स्ट्रिंग स्ट्रिंग में स्ट्रिंग की सूची संग्रहीत करने का प्रयास कर रहे हैं। यह फिट नहीं है। कोई फर्क नहीं पड़ता कि आप चर का उपयोग कैसे करते हैं, कुछ टूट गया है।

wget_options='--mirror --no-host-directories'चर wget_optionsको एक स्ट्रिंग में सेट करता है जिसमें एक स्थान होता है। इस बिंदु पर, यह जानने का कोई तरीका नहीं है कि क्या अंतरिक्ष को एक विकल्प का हिस्सा माना जाता है, या विकल्पों के बीच एक विभाजक।

जब आप एक उद्धृत प्रतिस्थापन के साथ चर का उपयोग करते हैं wget "$wget_options", तो चर का मूल्य स्ट्रिंग के रूप में उपयोग किया जाता है। इसका मतलब है कि यह एक एकल पैरामीटर के रूप में पारित हो गया है wget, इसलिए यह एक ही विकल्प है। यह आपके मामले में टूट जाता है क्योंकि आपने इसे कई विकल्पों से मतलब रखा है।

जब आप एक निर्विवाद प्रतिस्थापन का उपयोग करते हैं wget $wget_options, तो स्ट्रिंग चर का मान "स्प्लिट + ग्लोब" उपनाम की एक विस्तार प्रक्रिया से गुजरता है:

  1. चर का मान लें और इसे व्हाट्सएप-सीमांकित भागों में विभाजित करें (यह मानते हुए कि आपने $IFSचर को संशोधित नहीं किया है )। यह स्ट्रिंग की एक मध्यवर्ती सूची में परिणाम है।
  2. मध्यवर्ती सूची के प्रत्येक तत्व के लिए, यदि यह एक वाइल्डकार्ड पैटर्न है जो एक या अधिक फ़ाइलों से मेल खाता है, तो उस तत्व को मिलान फ़ाइलों की सूची से प्रतिस्थापित करें।

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

Ksh, bash, yash और zsh में, आप एक ऐरे वेरिएबल का उपयोग कर सकते हैं। शेल शब्दावली में एक सरणी तार की एक सूची है, इसलिए जानकारी का कोई नुकसान नहीं है। एक सरणी चर बनाने के लिए, चर के मान को असाइन करते समय सरणी तत्वों के चारों ओर कोष्ठक लगाएं। सरणी के सभी तत्वों का उपयोग करने के लिए, उपयोग - यह एक सामान्यीकरण है , जो सरणी के तत्वों से एक सूची बनाता है। ध्यान दें कि आपको यहां भी दोहरे उद्धरण चिह्नों की आवश्यकता है, अन्यथा प्रत्येक तत्व विभाजन + ग्लोब से गुजरता है।"${VARIABLE[@]}""$@"

wget_options=(--mirror --no-host-directories --user-agent="I can haz spaces")
wget "${wget_options[@]}" 

सादे श में, कोई सरणी चर नहीं हैं। यदि आप स्थिति संबंधी तर्कों को खोने का बुरा नहीं मानते हैं, तो आप उनका उपयोग स्ट्रिंग्स की एक सूची को संग्रहीत करने के लिए कर सकते हैं।

set -- --mirror --no-host-directories --user-agent="I can haz spaces"
wget "$@" 

अधिक जानकारी के लिए, देखें कि मेरी शेल स्क्रिप्ट व्हॉट्सएप या अन्य विशेष पात्रों पर क्यों चोक करती है?


सादे श के लिए एक उपधारा स्थितीय तर्कों को संरक्षित करेगा (set -- ...; exec wget "$@" ...):।
जॉन कुगेलमैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.