इस पर कई सवालों पर चर्चा की गई है। मैं उन सभी मुद्दों को इकट्ठा करने की कोशिश करूंगा, जो मैं यहां ले सकता हूं। अंत में सन्दर्भ।
यह असफल क्यों होता है
कारण यह है कि आप उन समस्याओं का सामना कर रहे हैं शब्द विभाजन और तथ्य यह है कि चर से विस्तारित उद्धरण उद्धरण के रूप में कार्य नहीं करते हैं, लेकिन सिर्फ साधारण अक्षर हैं।
प्रश्न में प्रस्तुत मामले:
$ abc='ls -l "/tmp/test/my dir"'
यहाँ, $abc
विभाजित और ls
दो तर्क मिलते हैं "/tmp/test/my
और dir"
(पहले के मोर्चे पर उद्धरण के साथ और दूसरे के पीछे):
$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory
यहाँ, विस्तार उद्धृत किया गया है, इसलिए इसे एक ही शब्द के रूप में रखा गया है। शेल एक प्रोग्राम को खोजने की कोशिश करता है जिसे कहा जाता है ls -l "/tmp/test/my dir"
, रिक्त स्थान और उद्धरण शामिल हैं।
$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory
और यहाँ, केवल पहले शब्द या $abc
तर्क के रूप में लिया जाता है -c
, इसलिए बैश केवल ls
वर्तमान निर्देशिका में चलता है। दूसरे शब्दों बैश के लिए तर्क हैं, और भरने के लिए उपयोग किया जाता है $0
, $1
आदि
$ bash -c $abc
'my dir'
के साथ bash -c "$abc"
, और eval "$abc"
, एक अतिरिक्त शेल प्रोसेसिंग स्टेप है, जो उद्धरणों को काम करता है, लेकिन सभी शेल एक्सपेंशन को फिर से संसाधित करने का कारण बनता है , इसलिए उपयोगकर्ता द्वारा प्रदान किए गए डेटा से गलती से कमांड विस्तार से चलने का जोखिम है, जब तक कि आप बहुत अधिक न हों। उद्धृत करने के बारे में सावधान।
इसे करने के बेहतर तरीके
कमांड को स्टोर करने के दो बेहतर तरीके हैं a) इसके बजाय फंक्शन का उपयोग करें, b) एक ऐरे वेरिएबल (या पोजिशनल पैरामीटर) का उपयोग करें।
एक समारोह का उपयोग करना:
बस कमांड के साथ एक फंक्शन की घोषणा करें, और फंक्शन को ऐसे चलाएं जैसे कि यह एक कमांड हो। फ़ंक्शन के भीतर आदेशों में विस्तार केवल तभी संसाधित होता है जब कमांड चलता है, तब नहीं जब इसे परिभाषित किया गया हो, और आपको अलग-अलग कमांडों को उद्धृत करने की आवश्यकता नहीं है।
# define it
myls() {
ls -l "/tmp/test/my dir"
}
# run it
myls
एक सरणी का उपयोग करना:
Arrays बहु-शब्द चर बनाने की अनुमति देता है जहां व्यक्तिगत शब्दों में सफेद स्थान होता है। यहां, अलग-अलग शब्दों को अलग-अलग सरणी तत्वों के रूप में संग्रहीत किया जाता है, और "${array[@]}"
विस्तार प्रत्येक तत्व को अलग-अलग शेल शब्दों के रूप में विस्तारित करता है:
# define the array
mycmd=(ls -l "/tmp/test/my dir")
# run the command
"${mycmd[@]}"
वाक्य-विन्यास थोड़ा भयानक है, लेकिन सरणियाँ आपको कमांड लाइन टुकड़ा-दर-टुकड़ा बनाने की अनुमति भी देती हैं। उदाहरण के लिए:
mycmd=(ls) # initial command
if [ "$want_detail" = 1 ]; then
mycmd+=(-l) # optional flag
fi
mycmd+=("$targetdir") # the filename
"${mycmd[@]}"
या कमांड लाइन के कुछ हिस्सों को स्थिर रखें और सरणी का उपयोग करें, इसके कुछ भाग, विकल्प या फ़ाइल नाम भरें:
options=(-x -v)
files=(file1 "file name with whitespace")
target=/somedir
transmutate "${options[@]}" "${files[@]}" "$target"
सरणियों का नकारात्मक पक्ष यह है कि वे एक मानक विशेषता नहीं हैं, इसलिए सादे POSIX गोले (जैसे dash
, /bin/sh
डेबियन / उबंटू में डिफ़ॉल्ट ) उनका समर्थन नहीं करते हैं (लेकिन नीचे देखें)। हालांकि, बैश, ksh और zsh करते हैं, इसलिए यह संभव है कि आपके सिस्टम में कुछ शेल हो जो ऐरे का समर्थन करता हो।
का उपयोग करते हुए "$@"
नामित सरणियों के समर्थन के बिना गोले में, कोई अभी भी "$@"
एक कमांड के तर्कों को रखने के लिए स्थितीय मापदंडों (छद्म-सरणी ) का उपयोग कर सकता है ।
निम्नलिखित पोर्टेबल स्क्रिप्ट बिट्स होना चाहिए जो पिछले अनुभाग में कोड बिट्स के बराबर करते हैं। सरणी को स्थितिगत "$@"
मापदंडों की सूची से बदल दिया जाता है । सेटिंग के "$@"
साथ किया जाता है set
, और आसपास के दोहरे उद्धरण "$@"
महत्वपूर्ण होते हैं (ये सूची के तत्वों को व्यक्तिगत रूप से उद्धृत किया जाता है)।
सबसे पहले, केवल तर्कों के साथ एक कमांड स्टोर करना "$@"
और इसे चलाना:
set -- ls -l "/tmp/test/my dir"
"$@"
कमांड के लिए कमांड लाइन विकल्प के सक्रिय रूप से भागों की स्थापना:
set -- ls
if [ "$want_detail" = 1 ]; then
set -- "$@" -l
fi
set -- "$@" "$targetdir"
"$@"
केवल "$@"
विकल्प और ऑपरेंड के लिए उपयोग करना:
set -- -x -v
set -- "$@" file1 "file name with whitespace"
set -- "$@" /somedir
transmutate "$@"
(ज़ाहिर है, "$@"
आमतौर पर स्क्रिप्ट के लिए तर्कों से भरा होता है, इसलिए आपको पुन: प्रयोजन से पहले उन्हें कहीं और सहेजना होगा "$@"
।)
से सावधान eval
!
जैसा कि eval
एक अतिरिक्त स्तर के उद्धरण और विस्तार प्रसंस्करण का परिचय देता है, आपको उपयोगकर्ता इनपुट से सावधान रहने की आवश्यकता है। उदाहरण के लिए, यह तब तक काम करता है जब तक उपयोगकर्ता किसी एक उद्धरण में टाइप नहीं करता है:
read -r filename
cmd="ls -l '$filename'"
eval "$cmd";
लेकिन अगर वे इनपुट देते हैं '$(uname)'.txt
, तो आपकी स्क्रिप्ट खुशी से कमांड प्रतिस्थापन चलाती है।
सरणियों के साथ एक संस्करण उस के लिए प्रतिरक्षा है क्योंकि पूरे समय के लिए शब्दों को अलग रखा जाता है, सामग्री की सामग्री के लिए कोई उद्धरण या अन्य प्रसंस्करण नहीं है filename
।
read -r filename
cmd=(ls -ld -- "$filename")
"${cmd[@]}"
संदर्भ