@ कुसलानंद ने पहले से ही मूल समस्या और इसे हल करने के तरीके के बारे में बताया है, और @ एलगेन जैकमैन द्वारा लिंक की गई बैश एफएक्यू प्रविष्टि भी बहुत उपयोगी जानकारी प्रदान करती है। इन संसाधनों के आधार पर मेरी समस्या में क्या हो रहा है, इसका विस्तृत विवरण यहां दिया गया है।
हम एक छोटी स्क्रिप्ट का उपयोग करेंगे जो चीजों को चित्रित करने के लिए एक अलग लाइन पर अपने प्रत्येक तर्क को प्रिंट करती है ( argtest.bash
):
#!/bin/bash
for var in "$@"
do
echo "$var"
done
पासिंग विकल्प "मैन्युअल रूप से":
$ ./argtest.bash -rnv --exclude='.*'
-rnv
--exclude=.*
जैसी उम्मीद थी, भागों -rnv
और --exclude='.*'
, दो तर्क में विभाजित हैं के रूप में वे गैर उद्धृत खाली स्थान के द्वारा अलग किया जाता है (यह भी कहा जाता है शब्द बंटवारे )।
यह भी ध्यान दें कि आसपास के उद्धरण .*
हटा दिए गए हैं: एकल उद्धरण विशेष व्याख्या के बिना अपनी सामग्री को पारित करने के लिए शेल को बताते हैं , लेकिन खुद को उद्धरण कमांड में पारित नहीं किया जाता है ।
यदि हम अब एक चर में स्ट्रिंग के रूप में विकल्पों को संग्रहीत करते हैं (जैसा कि एक सरणी का उपयोग करने के लिए विरोध किया जाता है), तो उद्धरण हटाए नहीं जाते हैं :
$ OPTS="--exclude='.*'"
$ ./argtest.bash $OPTS
--exclude='.*'
यह दो कारणों से है: डबल कोट का उपयोग करते समय परिभाषित $OPTS
एकल उद्धरण के विशेष उपचार को रोकते हैं, इसलिए उत्तरार्द्ध मूल्य का हिस्सा हैं:
$ echo $OPTS
--exclude='.*'
जब हम अब $OPTS
एक कमांड के तर्क के रूप में उपयोग करते हैं तो पैरामीटर विस्तार से पहले उद्धरण संसाधित होते हैं , इसलिए उद्धरण में$OPTS
"बहुत देर से" होते हैं।
इसका मतलब यह है कि (मेरी मूल समस्या में) rsync
पैटर्न के '.*'
बजाय बहिष्कृत पैटर्न (उद्धरण के साथ!) का उपयोग करता है .*
- यह उन फ़ाइलों को बाहर करता है जिनका नाम एकल उद्धरण के साथ शुरू होता है और डॉट के साथ समाप्त होता है। जाहिर है कि ऐसा नहीं था।
परिभाषित करते समय वर्कअराउंड डबल कोट्स को छोड़ना होगा $OPTS
:
$ OPTS2=--exclude='.*'
$ ./argtest.bash $OPTS2
--exclude=.*
हालांकि, अधिक जटिल मामलों में सूक्ष्म अंतर के कारण हमेशा चर असाइनमेंट को उद्धृत करना एक अच्छा अभ्यास है ।
जैसा कि @ कुसलानंद ने कहा, उद्धृत .*
न करने से भी काम होता। मैंने पैटर्न विस्तार को रोकने के लिए उद्धरण जोड़े थे , लेकिन इस विशेष मामले में यह आवश्यक नहीं था :
$ ./argtest.bash --exclude=.*
--exclude=.*
यह पता चलता है कि बैश पैटर्न का विस्तार करता है , लेकिन पैटर्न --exclude=.*
किसी भी फाइल से मेल नहीं खाता है, इसलिए पैटर्न कमांड पर पारित किया जाता है। की तुलना करें:
$ touch some_file
$ ./argtest.bash some_*
some_file
$ ./argtest.bash does_not_exit_*
does_not_exit_*
हालाँकि, पैटर्न को उद्धृत करना खतरनाक नहीं है, क्योंकि यदि (जो भी कारण से) कोई फ़ाइल मिलान होता है --exclude=.*
तो पैटर्न का विस्तार हो जाता है:
$ touch -- --exclude=.special-filenames-happen
$ ./argtest.bash --exclude=.*
--exclude=.special-filenames-happen
अंत में, आइए देखें कि एक सरणी का उपयोग मेरी उद्धरण समस्या को क्यों रोकता है (कमांड तर्कों को संग्रहीत करने के लिए सरणियों का उपयोग करने के अन्य लाभों के अलावा)।
सरणी को परिभाषित करते समय, शब्द बंटवारा और उद्धरण हैंडलिंग उम्मीद के अनुसार होता है:
$ ARRAY_OPTS=( -rnv --exclude='.*' )
$ echo length of the array: "${#ARRAY_OPTS[@]}"
length of the array: 2
$ echo first element: "${ARRAY_OPTS[0]}"
first element: -rnv
$ echo second element: "${ARRAY_OPTS[1]}"
second element: --exclude=.*
कमांड के विकल्पों को पास करते समय, हम सिंटैक्स का उपयोग करते हैं "${ARRAY[@]}"
, जो एरे के प्रत्येक तत्व को एक अलग शब्द में विस्तारित करता है:
$ ./argtest.bash "${ARRAY_OPTS[@]}"
-rnv
--exclude=.*