अगर मुझे इको के लिए नहीं, बल्कि इसके लिए वैरिएबल क्यों उद्धृत करना है?


26

मैंने पढ़ा है कि आपको चर के विस्तार के लिए दोहरे उद्धरण चिह्नों की आवश्यकता है, जैसे

if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi

उम्मीद के मुताबिक काम करेंगे, जबकि

if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi

हमेशा कहेंगे $test okभले ही $testअशक्त हो।

लेकिन फिर हमें उद्धरणों की आवश्यकता क्यों नहीं है echo $test?


2
यदि आप एक चर को अर्ग के रूप में उपयोग करने के लिए उद्धृत नहीं करते हैं echo, तो अतिरिक्त रिक्त स्थान और नए अंक छीन लिए जाएंगे।
जोर्डन

जवाबों:


36

आपको हमेशा सभी सूची संदर्भों में चर के आसपास उद्धरणों की आवश्यकता होती है, जो कि हर जगह चर को कई मूल्यों तक विस्तारित किया जा सकता है जब तक कि आप नहीं चाहते कि एक चर को छोड़ने के 3 दुष्प्रभाव।

सूची संदर्भों में साधारण आदेशों जैसे तर्क शामिल हैं [या echo, for i in <here>सरणियों को असाइनमेंट ... अन्य संदर्भ हैं जहां चर भी उद्धृत किए जाने की आवश्यकता है। जब तक आपको बहुत अच्छा कारण नहीं मिल जाता है, तब तक हमेशा चर को उद्धृत करना सबसे अच्छा है।

विभाजित + ग्लोब ऑपरेटर के रूप में उद्धरण (सूची संदर्भों में) की अनुपस्थिति के बारे में सोचें ।

मानो echo $testथा echo glob(split("$test"))

शेल का व्यवहार अधिकांश लोगों को भ्रमित कर रहा है क्योंकि अधिकांश अन्य भाषाओं में, आप निश्चित स्ट्रिंग्स के आसपास उद्धरण देते हैं, जैसे कि puts("foo"), और न ही चर के आसपास (जैसे puts(var)) जबकि शेल में यह दूसरा तरीका है गोल: सब कुछ शेल में स्ट्रिंग है, इसलिए सब कुछ के आसपास उद्धरण डाल रहा है बोझिल हो जाएगा, आप echo test, आप की जरूरत नहीं है "echo" "test"। शेल में, उद्धरण कुछ और के लिए उपयोग किए जाते हैं: कुछ वर्णों के कुछ विशेष अर्थ को रोकते हैं और / या कुछ विस्तार के व्यवहार को प्रभावित करते हैं।

में [ -n $test ]या echo $test, खोल विभाजित कर देगा $test(सभी का विस्तार (डिफ़ॉल्ट रूप से रिक्त स्थान पर), और फिर फ़ाइल नाम पीढ़ी प्रदर्शन *मिलान फ़ाइलों की सूची के लिए, '?' ... पैटर्न), और फिर करने के लिए तर्क की उस सूची से पारित [या echoआदेशों ।

फिर से, के रूप में इसके बारे में सोचो "[" "-n" glob(split("$test")) "]"। यदि $testखाली है या केवल रिक्त स्थान (spc, tab, nl) शामिल है, तो विभाजन + ग्लोब ऑपरेटर एक खाली सूची लौटाएगा, इसलिए [ -n $test ]वसीयत होगी "[" "-n" "]", जो कि wheter की जांच करने के लिए एक परीक्षण है "-n" खाली स्ट्रिंग है या नहीं। लेकिन सोचिए अगर $test"*" या "= फू" हो तो क्या होता ...

में [ -n "$test" ], [चार तर्क पारित हो जाता है "[", "-n", ""और "]"(उद्धरण के बिना) है, जो है कि हम क्या चाहते हैं।

चाहे वह कोई अंतर रखता हो echoया [नहीं, यह सिर्फ echoएक ही बात का उत्पादन करता है कि क्या यह एक खाली तर्क है या कोई तर्क नहीं है।

कमांड और निर्माण पर अधिक विवरण के लिए एक समान प्रश्न के लिए इस उत्तर को भी देखें ।[[[...]]


7

@ h3rmiller का उत्तर यह समझाने के लिए अच्छा है कि आपको if(या बल्कि, [/ test) के लिए उद्धरणों की आवश्यकता क्यों है , लेकिन मैं वास्तव में यह स्वीकार करूंगा कि आपका प्रश्न गलत है।

निम्न आदेशों का प्रयास करें, और आप देखेंगे कि मेरा क्या मतलब है।

export testvar="123    456"
echo $testvar
echo "$testvar"

उद्धरणों के बिना, चर प्रतिस्थापन दूसरे कमांड का विस्तार करने का कारण बनता है:

echo 123    456

और एक ही स्थान पर कई रिक्त स्थान ढह गए हैं:

echo 123 456

उद्धरण के साथ, रिक्त स्थान संरक्षित हैं।

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

यह निम्नलिखित (बहुत सरल) सी कार्यक्रम द्वारा सचित्र किया जा सकता है। कमांड लाइन पर निम्नलिखित का प्रयास करें (आप इसे खाली निर्देशिका में करना चाह सकते हैं ताकि कुछ को अधिलेखित करने के लिए जोखिम न हो)।

cat <<EOF >paramtest.c
#include <stdio.h>
int main(int argc, char **argv) {
  int nparams = argc-1; /* because 1 parameter means only the executable's name */
  printf("%d parameters received\n", nparams);
  return nparams;
}
EOF
cc -o paramtest paramtest.c

और फिर...

./paramtest 123 456
./paramtest "123 456"
./paramtest 123   456
./paramtest "123   456"

चलाने के बाद paramtest, $?यह पारित किए गए मापदंडों की संख्या को धारण करेगा (और वह संख्या मुद्रित की जाएगी)।


2

यह सब एक कार्यक्रम को निष्पादित करने से पहले शेल लाइन की व्याख्या कैसे करता है, इसके बारे में है।

यदि रेखा पढ़ती है echo I am $USER, तो शेल इसका विस्तार करता है echo I am blrflऔर echoइसका कोई सुराग नहीं है कि क्या पाठ की उत्पत्ति एक शाब्दिक या एक चर विस्तार है। इसी तरह, यदि कोई रेखा पढ़ती है echo I am $UNDEFINED, तो शेल $UNDEFINEDकुछ भी नहीं में विस्तार करेगा और गूंज के तर्क होंगे I am, और यह इसका अंत है। चूँकि echoबिना किसी तर्क के ठीक काम करता है, echo $UNDEFINEDपूरी तरह से मान्य है।

आपका मुद्दा ifवास्तव में साथ नहीं है if, क्योंकि ifजो भी प्रोग्राम और तर्क इसका अनुसरण करता है, और thenयदि प्रोग्राम से बाहर निकलता है 0(या elseयदि कोई एक है और प्रोग्राम गैर-बाहर निकलता है तो भाग निष्पादित होता है 0):

if /bin/true ; then echo True dat. ; fi
if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi

जब आप if [ ... ]तुलना करने के लिए उपयोग करते हैं, तो आप शेल में निर्मित प्राइमिटिव का उपयोग नहीं कर रहे हैं। आप वास्तव में शेल को एक प्रोग्राम चलाने का निर्देश दे रहे हैं, जिसे कहा जाता है [कि यह बहुत मामूली सुपरसेट है test(1), इसके अंतिम तर्क की आवश्यकता है ]0यदि परीक्षण की स्थिति सही निकली और 1यदि ऐसा नहीं हुआ तो दोनों कार्यक्रम से बाहर निकल जाते हैं।

एक चर अपरिभाषित होने पर कुछ परीक्षण टूटने का कारण testयह है कि आप चर का उपयोग नहीं कर रहे हैं। एर्गो, [ $UNDEFINED -eq 2 ]टूट जाता है क्योंकि जब तक शेल इसके साथ किया जाता है, सभी testतर्क के लिए देखते हैं -eq 2 ], जो एक वैध परीक्षण नहीं है। यदि आपने इसे परिभाषित कुछ के साथ किया है, जैसे कि [ $DEFINED -ne 0 ], यह काम करेगा क्योंकि शेल इसे एक वैध परीक्षण (जैसे, 0 -ne 0) में विस्तारित करेगा ।

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


0

उद्धरण के बिना $testएक से अधिक शब्दों का विस्तार हो सकता है, इसलिए इसे वाक्यविन्यास को तोड़ने के लिए उद्धृत करने की आवश्यकता है क्योंकि [कमांड के अंदर प्रत्येक स्विच एक तर्क की उम्मीद कर रहा है जो उद्धरण है (जो भी $testएक तर्क में विस्तार करता है)

जिस कारण से आपको किसी चर का विस्तार करने के लिए उद्धरण चिह्नों की आवश्यकता नहीं echoहै, क्योंकि यह एक तर्क की अपेक्षा नहीं कर रहा है। यह केवल वह प्रिंट करेगा जो आप इसे बताते हैं। यहां तक ​​कि अगर $test100 शब्द तक फैलता है तो भी इको प्रिंट होगा।

बैश नुकसान पर एक नज़र


हाँ, लेकिन हमें इसकी आवश्यकता क्यों नहीं है echo?
चार्ल्सबी

@CharlesB आपको उद्धरण चिह्नों की आवश्यकता है echo। आपको क्या लगता है अन्यथा?
गिलेस एसओ- बुराई को रोकना '

मुझे उनकी आवश्यकता नहीं है, मैं कर सकता हूँ echo $testऔर यह काम करता है (यह $ परीक्षण के मूल्य को आउटपुट करता है)
चार्ल्स

1
@CharlesB यह केवल $ परीक्षण के मूल्य को आउटपुट करता है अगर इसमें कहीं भी कई स्थान नहीं हैं। कारण के चित्रण के लिए मेरे उत्तर में कार्यक्रम का प्रयास करें।
एक CVn

0

खाली पैरामीटर उद्धृत न किए जाने पर हटा दिए जाते हैं:

start cmd:> strace -e trace=execve echo foo $bar baz
execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

start cmd:> strace -e trace=execve echo foo "$bar" baz
execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0

कहा जाता कमांड यह नहीं देखता है कि शेल कमांड लाइन पर एक खाली पैरामीटर था। ऐसा लगता है कि [0 परिभाषित करने के लिए 0 पर लौटने के लिए परिभाषित किया गया है जिसमें कुछ भी नहीं है। क्यों कभी।

उद्धरण कई मामलों में भी, गूंज के लिए एक अंतर बनाता है:

var='*'
echo $var
echo "$var"

var="foo        bar"
echo $var
echo "$var"

2
यह नहीं है echo, यह खोल है। आप के साथ एक ही व्यवहार देखेंगे lstouch '*'कुछ समय का प्रयास करें यदि आप साहसी महसूस करते हैं। :)
बजे एक CVn

यह सिर्फ इतना है कि 'अगर [...] `के मामले में कोई अंतर नहीं है। [एक विशेष शेल कमांड नहीं है। यह [[(कोस में) से अलग है जहाँ उद्धृत करना आवश्यक नहीं है।
हॉके लैजिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.