मैंने अभी एक वेरिएबल असाइन किया है, लेकिन इको $ वेरिएबल कुछ और दिखाता है


104

यहां उन मामलों की एक श्रृंखला दी गई है echo $var, जो अभी सौंपे गए से अलग मूल्य दिखा सकते हैं। यह इस बात की परवाह किए बिना होता है कि निर्दिष्ट मूल्य "दोहरे उद्धरण", 'एकल उद्धृत' या निर्विवाद था।

मुझे अपने चर को सही ढंग से सेट करने के लिए शेल कैसे मिलेगा?

तारांकन

अपेक्षित आउटपुट है /* Foobar is free software */, लेकिन इसके बजाय मुझे फ़ाइल नाम की एक सूची मिलती है:

$ var="/* Foobar is free software */"
$ echo $var 
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...

वर्ग कोष्ठक

अपेक्षित मूल्य है [a-z], लेकिन कभी-कभी मुझे इसके बजाय एक ही पत्र मिलता है!

$ var=[a-z]
$ echo $var
c

लाइन फीड (नई लाइनें)

अपेक्षित मान अलग लाइनों की एक सूची है, लेकिन इसके बजाय सभी मान एक पंक्ति में हैं!

$ cat file
foo
bar
baz

$ var=$(cat file)
$ echo $var
foo bar baz

कई जगह

मुझे सावधानी से संरेखित तालिका शीर्षलेख की उम्मीद थी, लेकिन इसके बजाय कई रिक्त स्थान या तो गायब हो गए या एक में ढह गए!

$ var="       title     |    count"
$ echo $var
title | count

टैब्स

मुझे दो टैब अलग मूल्यों की उम्मीद थी, लेकिन इसके बजाय मुझे दो अंतरिक्ष अलग मूल्य मिले!

$ var=$'key\tvalue'
$ echo $var
key value

2
ऐसा करने के लिए धन्यवाद। मैं लाइन का सामना अक्सर एक से करता हूं। तो var=$(cat file)ठीक है, लेकिन echo "$var"जरूरत है।
snd



इसके अलावा, stackoverflow.com/questions/2414150/…
tripleee

जवाबों:


139

उपरोक्त सभी मामलों में, चर सही ढंग से सेट किया गया है, लेकिन सही ढंग से पढ़ा नहीं गया है! संदर्भित करते समय दोहरा उद्धरण का उपयोग करने का सही तरीका है :

echo "$var"

यह दिए गए सभी उदाहरणों में अपेक्षित मूल्य देता है। हमेशा चर संदर्भ उद्धृत करें!


क्यों?

जब कोई चर अयोग्य है , तो यह होगा:

  1. फ़ील्ड को विभाजित करने से गुजरना जहां मूल्य को व्हाट्सएप पर कई शब्दों में विभाजित किया जाता है (डिफ़ॉल्ट रूप से):

    इससे पहले: /* Foobar is free software */

    बाद में: /*, Foobar,is , free, software,*/

  2. इनमें से प्रत्येक शब्द गुजरना होगा pathname विस्तार , जहां पैटर्न मिलान फ़ाइलों में विस्तारित हैं:

    इससे पहले: /*

    बाद में: /bin, /boot, /dev, /etc,/home , ...

  3. अंत में, सभी तर्क गूंज के लिए पारित किए जाते हैं, जो उन्हें एकल रिक्त स्थान द्वारा अलग-अलग लिखता है , दे रहा है

    /bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/

    चर के मूल्य के बजाय।

जब चर को उद्धृत किया जाता है किया जाएगा तो यह होगा:

  1. इसके मूल्य के लिए प्रतिस्थापित किया जाए।
  2. कोई चरण 2 नहीं है।

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


यह हमेशा काम नहीं कर रहा है। मैं एक उदाहरण दे सकता हूं: paste.ubuntu.com/p/8RjR6CS668
3

1
हाँ, $(..)स्ट्रिपिंग लाइनफीड्स। आप var=$(cat file; printf x); var="${var%x}"इसके आसपास काम करने के लिए उपयोग कर सकते हैं ।
वह दूसरा लड़का

17

आप जानना चाह सकते हैं कि ऐसा क्यों हो रहा है। उस दूसरे आदमी द्वारा महान विवरण के साथ , व्हॉट्सएप या अन्य विशेष वर्णों पर मेरे शेल स्क्रिप्ट चोक क्यों करता है का एक संदर्भ खोजें? यूनिक्स एंड लिनक्स में गाइल्स द्वारा लिखित :

मुझे लिखने की आवश्यकता क्यों है "$foo"? उद्धरणों के बिना क्या होता है?

$fooइसका मतलब यह नहीं है "चर का मूल्य ले लो foo"। इसका अर्थ है कुछ और अधिक जटिल:

  • सबसे पहले, वैरिएबल का मान लें।
  • फ़ील्ड विभाजन: उस मान को व्हाट्सएप द्वारा अलग किए गए फ़ील्ड की सूची के रूप में मानते हैं, और परिणामी सूची का निर्माण करते हैं। उदाहरण के लिए, चर शामिल करता है, तो foo * bar ​उसके बाद इस कदम का परिणाम 3-तत्व सूची है foo, *, bar
  • फ़ाइल नाम पीढ़ी: प्रत्येक फ़ील्ड को एक ग्लोब के रूप में अर्थात वाइल्डकार्ड पैटर्न के रूप में मानें, और फ़ाइल नामों की सूची से प्रतिस्थापित करें जो इस पैटर्न से मेल खाते हैं। यदि पैटर्न किसी भी फाइल से मेल नहीं खाता है, तो इसे अनमॉडिफाइड छोड़ दिया जाता है। हमारे उदाहरण fooमें, वर्तमान निर्देशिका में फ़ाइलों की सूची के बाद, और अंत में, सूची में यह परिणाम है bar। यदि वर्तमान निर्देशिका खाली है, परिणाम है foo, *, bar

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

वही इसके साथ $(foo)या उसके साथ कमांड प्रतिस्थापन के लिए होता है `foo`। एक साइड नोट पर, उपयोग न करें `foo`: इसके उद्धरण नियम अजीब और गैर-पोर्टेबल हैं, और सभी आधुनिक गोले समर्थन करते हैं$(foo) जो सहज ज्ञान युक्त उद्धरण नियमों को छोड़कर बिल्कुल समान है।

अंकगणितीय प्रतिस्थापन का उत्पादन भी समान विस्तार से गुजरता है, लेकिन यह आमतौर पर एक चिंता का विषय नहीं है क्योंकि इसमें केवल गैर-विस्तार योग्य वर्ण IFSहोते हैं (यह मानते हुए अंक या नहीं होते हैं -)।

देखें कि डबल-कोटिंग कब आवश्यक है? उन मामलों के बारे में अधिक जानकारी के लिए जब आप उद्धरण छोड़ सकते हैं।

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


7

उद्धरण के लिए असफल होने के कारण अन्य मुद्दों के अलावा, -nऔर तर्क के रूप में -eसेवन किया जा सकता echoहै। (केवल पूर्व के लिए POSIX युक्ति प्रति कानूनी है echo, लेकिन कई सामान्य कार्यान्वयन युक्ति का उल्लंघन करते हैं और -eसाथ ही उपभोग करते हैं )।

इससे बचने के लिए, विवरण के मामले के बजाय उपयोग करें ।printfecho

इस प्रकार:

$ vars="-e -n -a"
$ echo $vars      # breaks because -e and -n can be treated as arguments to echo
-a
$ echo "$vars"
-e -n -a

हालाँकि, सही उद्धरण हमेशा उपयोग करते समय आपको नहीं बचाएगा echo:

$ vars="-n"
$ echo $vars
$ ## not even an empty line was printed

... जबकि यह आपको बचाएगा printf:

$ vars="-n"
$ printf '%s\n' "$vars"
-n

हाँ, हमें इसके लिए एक अच्छे डेडअप की जरूरत है! मैं मानता हूं कि यह प्रश्न शीर्षक फिट बैठता है, लेकिन मुझे नहीं लगता कि इसे वह दृश्यता मिलेगी जो इसके लायक है। एक नए प्रश्न के बारे में कैसे ला "मेरे -e/ -n/ बैकस्लैश क्यों नहीं दिख रहा है?" हम उपयुक्त के रूप में यहाँ से लिंक जोड़ सकते हैं।
वह अन्य लड़का

क्या आपका मतलब उपभोग के -nरूप में भी था ?
पेसा

1
@PesaThe, नहीं, मेरा मतलब था -eechoजब इसका पहला तर्क होता है -n, तो मानक उस मानक को निर्दिष्ट नहीं करता है , जो उस मामले में किसी भी / सभी संभव आउटपुट को कानूनी बनाता है; के लिए ऐसा कोई प्रावधान नहीं है -e
चार्ल्स डफी

ओह ... मैं नहीं पढ़ सकता। उसके लिए मेरी अंग्रेजी को दोष देना। स्पष्टीकरण के लिए धन्यवाद।
पेसा

6

सटीक मूल्य प्राप्त करने के लिए उपयोगकर्ता दोहरा उद्धरण। इस तरह:

echo "${var}"

और यह आपके मूल्य को सही ढंग से पढ़ेगा।


काम करता है .. आभार
Tshilidzi Mudau

2

echo $varउत्पादन अत्यधिक IFSचर के मूल्य पर निर्भर करता है । डिफ़ॉल्ट रूप से इसमें स्थान, टैब और न्यूलाइन वर्ण होते हैं:

[ks@localhost ~]$ echo -n "$IFS" | cat -vte
 ^I$

इसका मतलब यह है कि जब शेल क्षेत्र विभाजन (या शब्द विभाजन) कर रहा है तो यह इन सभी वर्णों को शब्द विभाजक के रूप में उपयोग करता है। ऐसा तब होता है जब एक प्रतिलेख को दोहराए बिना एक चर का संदर्भ दिया जाता है ($var ) और इस प्रकार अपेक्षित आउटपुट में बदलाव किया जाता है।

शब्द विभाजन को रोकने के लिए (दोहरे उद्धरण चिह्नों का उपयोग करने के अलावा) IFSशून्य करने के लिए सेट करना है। Http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 देखें :

यदि IFS का मान शून्य है, तो कोई फ़ील्ड विभाजन नहीं किया जाएगा।

शून्य पर सेट करने का अर्थ है रिक्त मान पर सेट करना:

IFS=

परीक्षा:

[ks@localhost ~]$ echo -n "$IFS" | cat -vte
 ^I$
[ks@localhost ~]$ var=$'key\nvalue'
[ks@localhost ~]$ echo $var
key value
[ks@localhost ~]$ IFS=
[ks@localhost ~]$ echo $var
key
value
[ks@localhost ~]$ 

2
आपको set -fग्लोबिंग को भी रोकना होगा
उस अन्य व्यक्ति

@thatotherguy, क्या पथ विस्तार के साथ आपके 1-सेंट उदाहरण के लिए वास्तव में आवश्यक है? IFSअशक्त करने के लिए सेट के साथ , echo $varविस्तार किया जाएगा echo '/* Foobar is free software */'और पथ विस्तार एकल उद्धृत स्ट्रिंग्स के अंदर नहीं किया जाता है।
ks1322

1
हाँ। यदि आप mkdir "/this thing called Foobar is free software etc/"देखेंगे कि यह अभी भी फैलता है। यह स्पष्ट रूप से [a-z]उदाहरण के लिए अधिक व्यावहारिक है ।
वह अन्य लड़का

मैं देखता हूं, यह [a-z]उदाहरण के लिए समझ में आता है ।
ks1322

2

Ks1322 से जवाब मुझे मदद की समस्या को पहचानने के लिए उपयोग करते समय docker-compose exec:

यदि आप -Tध्वज को छोड़ देते हैं ,docker-compose exec एक विशेष वर्ण जोड़ें जो आउटपुट को तोड़ता है, हम इसके bबजाय देखते हैं 1b:

$ test=$(/usr/local/bin/docker-compose exec db bash -c "echo 1")
$ echo "${test}b"
b
echo "${test}" | cat -vte
1^M$

-Tध्वज के साथ , docker-compose execउम्मीद के मुताबिक काम करता है:

$ test=$(/usr/local/bin/docker-compose exec -T db bash -c "echo 1")
$ echo "${test}b"
1b

-2

चर को कोटेशन में रखने के लिए अतिरिक्त, चर का उत्पादन trऔर न्यूलाइन में रिक्त स्थान को परिवर्तित करने के लिए अनुवाद का उत्पादन भी कर सकता है।

$ echo $var | tr " " "\n"
foo
bar
baz

यद्यपि यह थोड़ा अधिक जटिल है, लेकिन यह आउटपुट के साथ अधिक विविधता जोड़ता है क्योंकि आप किसी भी वर्ण को चरों के बीच विभाजक के रूप में स्थानापन्न कर सकते हैं।


2
लेकिन यह सभी स्थानों को नई सुर्खियों में ले जाता है। कोटिंग मौजूदा newlines और रिक्त स्थान को संरक्षित करता है।
user000001

सच है, हाँ। मुझे लगता है कि यह चर के भीतर क्या है पर निर्भर करता है। मैं वास्तव में trपाठ फ़ाइलों से सरणियाँ बनाने के लिए दूसरे तरीके का उपयोग करता हूं ।
एलियन

3
चर को ठीक से उद्धृत न करके एक समस्या का निर्माण करना और फिर उसके चारों ओर बाधा रहित अतिरिक्त प्रक्रिया के साथ काम करना अच्छी प्रोग्रामिंग नहीं है।
ट्रिपल

@ एलेक, ... गलत, क्या? trपाठ फ़ाइल से एक सरणी को ठीक से / सही ढंग से बनाने के लिए कोई ज़रूरत नहीं है - आप IFS सेट करके जो भी विभाजक निर्दिष्ट कर सकते हैं। उदाहरण के लिए: IFS=$'\n' read -r -d '' -a arrayname < <(cat file.txt && printf '\0')बैश 3.2 (वाइड सर्कुलेशन में सबसे पुराना संस्करण) के माध्यम से सभी तरह से वापस काम करता है, और यदि आपके catअसफल होने पर सही ढंग से निकास की स्थिति सेट करता है । और यदि आप चाहते हैं, तो कहना है कि नई सुर्खियों के बजाय टैब, आप बस के $'\n'साथ बदल देंगे $'\t'
चार्ल्स डफी

1
@Alek, ... आप की तरह कुछ कर रहे हैं arrayname=( $( cat file | tr '\n' ' ' ) ), तो उस की कई परतों पर टूट: यह अपने परिणामों को ग्लोबिंग है (ताकि एक *मौजूदा निर्देशिका में फ़ाइलों की एक सूची में बदल जाता है), और यह बस के रूप में अच्छी तरह से बिना काम करेगाtr ( या catउस बात के लिए, एक का उपयोग किया जा सकता है arrayname=$( $(<file) )और इसे उसी तरह से तोड़ा जाएगा, लेकिन कम अकुशल रूप से)।
चार्ल्स डफी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.