linux - एक शैल चर के चारों ओर उद्धरण लपेटने के लिए कब?



bash shell (3)

क्या कोई मुझे बता सकता है कि मुझे शेल स्क्रिप्ट में चर के चारों ओर उद्धरण लपेटना चाहिए या नहीं?

उदाहरण के लिए, निम्नलिखित सही है:

xdg-open $URL 
[ $? -eq 2 ]

या

xdg-open "$URL"
[ "$?" -eq "2" ]

और यदि हां, तो क्यों?


मैं आमतौर पर सुरक्षित के लिए "$var" जैसे उद्धृत का उपयोग करता हूं, जब तक कि मुझे यकीन न हो कि $var में स्पेस नहीं है।

मैं पंक्तियों में शामिल होने के लिए एक आसान तरीका के रूप में $var उपयोग करता हूं:

lines="`cat multi-lines-text-file.txt`"
echo "$lines"                             ## multiple lines
echo $lines                               ## all spaces (including newlines) are zapped

संक्षेप में, टोकन विभाजन और वाइल्डकार्ड विस्तार करने के लिए आपको खोलने की आवश्यकता नहीं है, जहां सब कुछ उद्धृत करें।

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

$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change

$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.

परिवर्तनीय इंटरपोलेशन की आवश्यकता होने पर डबल कोट्स उपयुक्त हैं। उपयुक्त अनुकूलन के साथ, यह स्ट्रिंग में सिंगल कोट्स की आवश्यकता होने पर भी एक अच्छा कामकाज है। (सिंगल कोट्स के बीच एक सिंगल कोट से बचने के लिए कोई सीधा तरीका नहीं है, क्योंकि सिंगल कोट्स के अंदर कोई बचने की तंत्र नहीं है - अगर वहां होता, तो वे पूरी तरह से शब्दशः उद्धृत नहीं करेंगे।)

$ echo "There is no place like '$HOME'"
There is no place like '/home/me'

जब आप विशेष रूप से टोकन विभाजन और / या वाइल्डकार्ड विस्तार करने के लिए खोल की आवश्यकता होती है तो कोई उद्धरण उपयुक्त नहीं होता है।

टोकन विभाजन;

 $ words="foo bar baz"
 $ for word in $words; do
 >   echo "$word"
 > done
 foo
 bar
 baz

इसके विपरीत:

 $ for word in "$words"; do echo "$word"; done
 foo bar baz

(लूप केवल एक बार, एकल, उद्धृत स्ट्रिंग पर चलता है।)

 $ for word in '$words'; do echo "$word"; done
 $words

(लूप केवल शाब्दिक सिंगल-उद्धृत स्ट्रिंग पर एक बार चलता है।)

वाइल्डकार्ड विस्तार:

$ pattern='file*.txt'
$ ls $pattern
file1.txt      file_other.txt

इसके विपरीत:

$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory

(शाब्दिक file*.txt नाम की कोई फ़ाइल नहीं है।)

$ ls '$pattern'
ls: cannot access $pattern: No such file or directory

( $pattern नामक कोई फ़ाइल नहीं है, या तो!)

अधिक ठोस शब्दों में, फ़ाइल नाम वाले किसी भी चीज़ को आमतौर पर उद्धृत किया जाना चाहिए (क्योंकि फ़ाइल नामों में व्हाइटस्पेस और अन्य शैल मेटाएक्टैक्टर्स हो सकते हैं)। यूआरएल वाले किसी भी चीज को आमतौर पर उद्धृत किया जाना चाहिए (क्योंकि कई यूआरएल में शैल मेटाएक्टेक्टर जैसे हैं ? और & )। रेगेक्स युक्त कुछ भी आमतौर पर उद्धृत किया जाना चाहिए (ditto ditto)। गैर-व्हाइटस्पेस अक्षरों के बीच एकल रिक्त स्थान के अलावा महत्वपूर्ण व्हाइटस्पेस वाले किसी भी चीज को उद्धृत करने की आवश्यकता है (क्योंकि अन्यथा, खोल सफेद जगह को प्रभावी ढंग से, एकल रिक्त स्थान में घुमाएगा, और किसी भी अग्रणी या पिछली सफेद जगह को ट्रिम करेगा)।

जब आप जानते हैं कि एक चर में केवल एक मान हो सकता है जिसमें कोई शेल मेटाएक्टेक्टर्स नहीं है, तो उद्धरण वैकल्पिक है। इस प्रकार, एक unquoted $? मूल रूप से ठीक है, क्योंकि इस चर में केवल एक ही संख्या हो सकती है। हालांकि, "$?" यह भी सही है, और सामान्य स्थिरता और शुद्धता के लिए अनुशंसित (हालांकि यह मेरी व्यक्तिगत सिफारिश है, व्यापक रूप से मान्यता प्राप्त नीति नहीं)।

वे मान जो चर नहीं हैं मूल रूप से एक ही नियमों का पालन करते हैं, हालांकि आप उन्हें उद्धृत करने के बजाय किसी मेटाएक्टएक्टर्स से भी बच सकते हैं। एक सामान्य उदाहरण के लिए, जब तक मेटाएक्टेक्टर से बच निकला या उद्धृत नहीं किया जाता है, तब तक इसमें एक & उस URL के साथ एक यूआरएल को पृष्ठभूमि कमांड के रूप में पार्स किया जाएगा:

$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found

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

wget 'http://example.com/q&uack'  # Single quotes preferred for a static string
wget "http://example.com/q&uack"  # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack   # Backslash escape
wget http://example.com/q'&'uack  # Only the metacharacter really needs quoting

आखिरी उदाहरण एक और उपयोगी अवधारणा का भी सुझाव देता है, जिसे मैं "देखा उद्धरण" कहता हूं। यदि आपको एकल और डबल कोट्स मिश्रण करने की आवश्यकता है, तो आप उन्हें एक-दूसरे के साथ उपयोग कर सकते हैं। उदाहरण के लिए, निम्नलिखित उद्धृत तार

'$HOME '
"isn't"
' where `<3'
"' is."

टोकननाइजेशन और उद्धरण हटाने के बाद एक सिंगल लंबी स्ट्रिंग बनाने, वापस एक साथ चिपकाया जा सकता है।

$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.

यह बहुत ही सुगम नहीं है, लेकिन यह एक आम तकनीक है और इस तरह जानना अच्छा है।

एक तरफ के रूप में, स्क्रिप्ट आमतौर पर किसी भी चीज़ के लिए ls उपयोग नहीं करना चाहिए। वाइल्डकार्ड का विस्तार करने के लिए, बस ... इसका इस्तेमाल करें।

$ printf '%s\n' $pattern   # not ``ls -1 $pattern''
file1.txt
file_other.txt

$ for file in $pattern; do  # definitely, definitely not ``for file in $(ls $pattern)''
>  printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt

(लूप बाद के उदाहरण में पूरी तरह से अनिवार्य है; printf विशेष रूप से कई तर्कों के साथ ठीक काम करता है। stat भी। लेकिन वाइल्डकार्ड मैच पर लूपिंग एक आम समस्या है, और अक्सर गलत तरीके से किया जाता है।)

एक वैरिएबल जिसमें लूप को लूप ओवर या विस्तृत करने के लिए वाइल्डकार्ड की एक सूची होती है, को कम बार देखा जाता है, इसलिए हम कभी-कभी संक्षेप में "सब कुछ उद्धृत करते हैं जब तक आप यह नहीं जानते कि आप क्या कर रहे हैं"।


सामान्य रूप से उद्धरणों के लिए एक तीन-बिंदु सूत्र है:

डबल उद्धरण

संदर्भों में जहां हम शब्द विभाजन और ग्लोबिंग को दबाना चाहते हैं। इसके संदर्भ में जहां हम शाब्दिक को एक स्ट्रिंग के रूप में माना जाना चाहते हैं, रेगेक्स नहीं।

एकल कोट

स्ट्रिंग अक्षर में जहां हम इंटरपोलेशन और बैकस्लाश के विशेष उपचार को दबाना चाहते हैं। दूसरे शब्दों में, स्थितियों जहां डबल कोट्स का उपयोग अनुचित होगा।

कोई उद्धरण नहीं

संदर्भों में जहां हम पूरी तरह से सुनिश्चित हैं कि कोई शब्द विभाजन या ग्लोबिंग समस्या नहीं है या हम शब्द विभाजन और ग्लोबिंग चाहते हैं

उदाहरण

डबल उद्धरण

  • व्हाइटस्पेस के साथ शाब्दिक तार ( " rocks!" , "Steve's Apple" )
  • परिवर्तनीय विस्तार ( "$var" , "${arr[@]}" )
  • कमांड प्रतिस्थापन ( "$(ls)" , "`ls`" )
  • ग्लब्स जहां निर्देशिका पथ या फ़ाइल नाम भाग में रिक्त स्थान शामिल हैं ( "/my dir/"* )
  • एकल उद्धरणों की रक्षा के लिए ( "single'quote'delimited'string" एकल "single'quote'delimited'string" )
  • बैश पैरामीटर विस्तार ( "${filename##*/}" )

एकल कोट

  • कमांड नाम और तर्क जिनमें उनमें सफेद जगह है
  • शाब्दिक तारों को दबाए जाने के लिए इंटरपोलेशन की आवश्यकता होती है ( 'Really costs $$!' , 'just a backslash followed by at: \t' )
  • डबल कोट्स की रक्षा के लिए ( 'The "crux"' )
  • रेगेक्स अक्षर जिन्हें दबाने के लिए इंटरपोलेशन की आवश्यकता होती है
  • विशेष पात्रों ( $'\n\t' ) से जुड़े अक्षरों के लिए खोल उद्धरण का उपयोग करें
  • खोल उद्धरण का उपयोग करें जहां हमें कई एकल और डबल कोट्स ( $'{"table": "users", "where": "first_name"=\'Steve\'}' रक्षा करने की आवश्यकता है)

कोई उद्धरण नहीं

  • मानक संख्यात्मक चर के आसपास ( $$ , $? $# आदि)
  • अंकगणितीय संदर्भों में ((count++)) , "${arr[idx]}" , "${string:start:length}"
  • अंदर [[ ]] अभिव्यक्ति जो शब्द विभाजन और ग्लोबिंग मुद्दों से मुक्त है (यह शैली का विषय है और विचार व्यापक रूप से भिन्न हो सकते हैं)
  • जहां हम शब्द विभाजन चाहते हैं ( for word in $words )
  • जहां हम globbing चाहते हैं ( for txtfile in *.txt; do ... )
  • जहां हम ~ $HOME रूप में व्याख्या करना चाहते हैं ( ~/"some dir" लेकिन नहीं "~/some dir" )

यह भी देखें:

  • बैश में सिंगल और डबल कोट्स के बीच अंतर
  • विशेष डॉलर साइन शैल चर क्या हैं?




quotes