$ * और $ @ के बीच क्या अंतर है?


72

निम्नलिखित कोड पर विचार करें:

foo () {
    echo $*
}

bar () {
    echo $@
}

foo 1 2 3 4
bar 1 2 3 4

यह आउटपुट:

1 2 3 4

1 2 3 4

मैं Ksh88 का उपयोग कर रहा हूं, लेकिन मुझे अन्य सामान्य गोले में भी दिलचस्पी है। यदि आप विशिष्ट गोले के लिए किसी विशेषता को जानते हैं, तो कृपया उनका उल्लेख करें।

मुझे सोलारिस पर Ksh मैन पेज में follwing मिला:

$ * और $ @ का अर्थ समान है जब उद्धृत नहीं किया जाता है या जब पैरामीटर असाइनमेंट मान के रूप में या फ़ाइल नाम के रूप में उपयोग किया जाता है। हालांकि, जब एक कमांड तर्क के रूप में उपयोग किया जाता है, तो $ * `$ 1d $ 2d ... '' के बराबर होता है, जहां d IFS चर का पहला वर्ण है, जबकि $ @ $ 1 $ 2 के बराबर है ...।

मैंने IFSचर को संशोधित करने की कोशिश की , लेकिन यह आउटपुट को संशोधित नहीं करता है। शायद मैं कुछ गलत कर रहा हूँ?

जवाबों:


95

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


"$*"एक शब्द के लिए फैलता है "$1c$2c..."। आमतौर cपर एक स्थान है, लेकिन यह वास्तव में का पहला चरित्र है IFS, इसलिए यह आपके द्वारा चुने गए कुछ भी हो सकता है।

मैंने इसके लिए केवल एक अच्छा उपयोग किया है:

अल्पविराम (सरल संस्करण) के साथ बहस में शामिल हों

join1() {
    typeset IFS=,
    echo "$*"
}

join1 a b c   # => a,b,c

निर्दिष्ट परिसीमा (बेहतर संस्करण) के साथ बहस में शामिल हों

join2() {
    typeset IFS=$1   # typeset makes a local variable in ksh (see footnote)
    shift
    echo "$*"
}

join2 + a b c   # => a+b+c

"$@" शब्दों को अलग करने के लिए फैलता है: "$1" "$2" ...

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


चलिए एक स्क्रिप्ट लिखते हैं जो साथ svimचलती vimहै sudo। अंतर बताने के लिए हम तीन संस्करण करेंगे।

svim1

#!/bin/sh
sudo vim $*

svim2

#!/bin/sh
sudo vim "$*"

svim3

#!/bin/sh
sudo vim "$@"

वे सभी साधारण मामलों के लिए ठीक होंगे, उदाहरण के लिए एक एकल फ़ाइल नाम जिसमें रिक्त स्थान नहीं हैं:

svim1 foo.txt             # == sudo vim foo.txt
svim2 foo.txt             # == sudo vim "foo.txt"
svim2 foo.txt             # == sudo vim "foo.txt"

लेकिन केवल $*और "$@"ठीक से काम करें यदि आपके पास कई तर्क हैं।

svim1 foo.txt bar.txt     # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt     # == sudo vim "foo.txt bar.txt"   # one file name!
svim3 foo.txt bar.txt     # == sudo vim "foo.txt" "bar.txt"

और केवल "$*"और "$@"ठीक से काम करें अगर आपके पास रिक्त स्थान वाले तर्क हैं।

svim1 "shopping list.txt" # == sudo vim shopping list.txt   # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"

तो केवल "$@"हर समय ठीक से काम करेगा।


typesetकैसे एक स्थानीय चर बनाने के लिए है ksh( bashऔर इसके बजाय का ashउपयोग करें local)। इसका मतलब है IFSकि फ़ंक्शन वापस आने पर इसके पिछले मान को पुनर्स्थापित किया जाएगा। यह महत्वपूर्ण है, क्योंकि आपके द्वारा चलाए जाने वाले आदेश ठीक से काम नहीं कर सकते हैं यदि IFSकुछ गैर-मानक पर सेट किया गया है।


2
अद्भुत व्याख्या, बहुत-बहुत धन्यवाद।
रहमू

उपयोग करने के उदाहरण के लिए धन्यवाद $*। मैं हमेशा इसे पूरी तरह से बेकार मान रहा था ... सीमांकक के साथ जुड़ना एक अच्छा उपयोग मामला है।
ऐशसेन

35

संक्षिप्त उत्तर: उपयोग"$@" (दोहरे उद्धरण चिह्नों पर ध्यान दें)। अन्य रूप बहुत कम उपयोगी हैं।

"$@"एक अजीब वाक्यविन्यास है। यह अलग-अलग क्षेत्रों के रूप में, सभी स्थितिगत मापदंडों द्वारा प्रतिस्थापित किया जाता है। यदि कोई स्थितिगत पैरामीटर नहीं है ( $#0 है), तो "$@"कुछ भी नहीं फैलता है (खाली स्ट्रिंग नहीं है, लेकिन 0 तत्वों के साथ एक सूची है), यदि एक स्थितीय पैरामीटर है, तो "$@"इसके बराबर है "$1", यदि दो स्थितीय पैरामीटर हैं, तो "$@"इसके बराबर है "$1" "$2", आदि।

"$@"आपको किसी स्क्रिप्ट या फ़ंक्शन के तर्क को किसी अन्य कमांड को पास करने की अनुमति देता है। यह रैपर के लिए बहुत उपयोगी है जो पर्यावरण चर, डेटा फ़ाइल तैयार करना, आदि उन्हीं तर्कों और विकल्पों के साथ एक कमांड को कॉल करने से पहले करता है, जिनके साथ रैपर को बुलाया गया था।

उदाहरण के लिए, निम्न फ़ंक्शन आउटपुट को फ़िल्टर करता है cvs -nq update। आउटपुट फ़िल्टरिंग और रिटर्न स्टेटस (जो कि grepइसके बजाय है cvs) के अलावा, cvssmकुछ तर्कों पर कॉल cvs -nq updateकरना इन तर्कों के साथ कॉल करने जैसा व्यवहार करता है।

cvssm () { cvs -nq update "$@" | egrep -v '^[?A]'; }

"$@"स्थितिगत मापदंडों की सूची में विस्तार करता है। सरणियों का समर्थन करने वाले गोले में, सरणी के तत्वों की सूची का विस्तार करने के लिए एक समान सिंटैक्स है: "${array[@]}"(ब्रेसिज़ को ज़श को छोड़कर अनिवार्य है)। फिर से, दोहरे उद्धरण कुछ भ्रामक हैं: वे सरणी तत्वों के क्षेत्र विभाजन और पैटर्न पीढ़ी के खिलाफ रक्षा करते हैं, लेकिन प्रत्येक सरणी तत्व अपने क्षेत्र में समाप्त होता है।

कुछ प्राचीन गोले में यकीनन एक बग था: जब कोई स्थैतिक तर्क नहीं थे, "$@"एक खाली मैदान वाले एक क्षेत्र में विस्तारित किया गया था, न कि किसी क्षेत्र में। इसने वर्कअराउंड${1+"$@"} ( पर्ल दस्तावेज के माध्यम से प्रसिद्ध ) का नेतृत्व किया । वास्तविक बॉर्न शेल और OSF1 कार्यान्वयन के केवल पुराने संस्करण प्रभावित हैं, इसके आधुनिक संगत प्रतिस्थापन (राख, क्श, बाश, ...) में से कोई भी नहीं हैं। /bin/sh21 वीं सदी में जारी की गई किसी भी प्रणाली से प्रभावित नहीं है, जो मुझे पता है (जब तक आप Tru64 रखरखाव रिलीज की गिनती नहीं करते हैं, और यहां तक ​​कि /usr/xpg4/bin/shसुरक्षित है तो केवल #!/bin/shस्क्रिप्ट प्रभावित होती है, #!/usr/bin/env shस्क्रिप्ट नहीं जब तक कि आपका PATH POSIX अनुपालन के लिए सेट नहीं हो जाता) । संक्षेप में, यह एक ऐतिहासिक किस्सा है जिसके बारे में आपको चिंता करने की आवश्यकता नहीं है।


"$*"हमेशा एक शब्द के लिए फैलता है। इस शब्द में स्थितीय पैरामीटर हैं, जिन्हें बीच में एक स्थान के साथ समतल किया गया है। (आम तौर पर, विभाजक IFSचर के मूल्य का पहला वर्ण है । यदि मान IFSरिक्त स्ट्रिंग है, तो विभाजक रिक्त स्ट्रिंग है।) यदि कोई स्थितिगत पैरामीटर नहीं हैं "$*", तो रिक्त स्ट्रिंग है, यदि दो हैं। स्थितिगत मापदंडों और IFSउसके डिफ़ॉल्ट मान तो "$*"इसके बराबर है "$1 $2", आदि।

$@और $*बाहर के उद्धरण बराबर हैं। वे अलग-अलग क्षेत्रों की तरह, स्थितीय मापदंडों की सूची में विस्तार करते हैं "$@"; लेकिन प्रत्येक परिणामी फ़ील्ड को अलग-अलग फ़ील्ड्स में विभाजित किया जाता है, जिन्हें फ़ाइल नाम वाइल्डकार्ड पैटर्न के रूप में माना जाता है, हमेशा की तरह अनक्वॉटेड चर विस्तार के साथ।

उदाहरण के लिए, यदि वर्तमान निर्देशिका में तीन फ़ाइलें हैं bar, bazऔर foo, तब:

set --         # no positional parameters
for x in "$@"; do echo "$x"; done  # prints nothing
for x in "$*"; do echo "$x"; done  # prints 1 empty line
for x in $*; do echo "$x"; done    # prints nothing
set -- "b* c*" "qux"
echo "$@"      # prints `b* c* qux`
echo "$*"      # prints `b* c* qux`
echo $*        # prints `bar baz c* qux`
for x in "$@"; do echo "$x"; done  # prints 2 lines: `b* c*` and `qux`
for x in "$*"; do echo "$x"; done  # prints 1 lines: `b* c* qux`
for x in $*; do echo "$x"; done    # prints 4 lines: `bar`, `baz`, `c*` and `qux`

1
ऐतिहासिक नोट: कुछ प्राचीन बॉर्न गोले पर, "$@"वास्तव में खाली स्ट्रिंग की एक सूची का विस्तार करने के लिए किया था: unix.stackexchange.com/questions/68484/…
ninjalj

25

यहाँ एक सरल स्क्रिप्ट के बीच का अंतर बताता है $*और $@:

#!/bin/bash

test_param() {
  echo "Receive $# parameters"
  echo Using '$*'

  echo
  for param in $*; do
    printf '==>%s<==\n' "$param"
  done;

  echo
  echo Using '"$*"'
  for param in "$*"; do
    printf '==>%s<==\n' "$param"
  done;

  echo
  echo Using '$@'
  for param in $@; do
    printf '==>%s<==\n' "$param"
  done;

  echo
  echo Using '"$@"';
  for param in "$@"; do
  printf '==>%s<==\n' "$param"
  done
}

IFS="^${IFS}"

test_param 1 2 3 "a b c"

आउटपुट:

% cuonglm at ~
% bash test.sh
Receive 4 parameters

Using $*
==>1<==
==>2<==
==>3<==
==>a<==
==>b<==
==>c<==

Using "$*"
==>1^2^3^a b c<==

Using $@
==>1<==
==>2<==
==>3<==
==>a<==
==>b<==
==>c<==

Using "$@"
==>1<==
==>2<==
==>3<==
==>a b c<==

सरणी सिंटैक्स में, उपयोग करते समय $*या कोई अंतर नहीं होता है $@। यह केवल तभी समझ में आता है जब आप उन्हें दोहरे उद्धरण चिह्नों के साथ उपयोग करते हैं "$*"और "$@"


बहुत बढ़िया उदाहरण! आप के उपयोग की व्याख्या कर सकते हैं IFS="^${IFS}", हालांकि?
रस

@Russ: यह आपको दिखाता है कि पहले वर्ण के साथ कॉनसैट कितना महत्वपूर्ण है IFS
कोउंगलम

उसी तरह जो IFS="^xxxxx"करेगा? अनुगामी ${IFS}प्रत्यय ने मुझे लगता है कि आप कुछ पेचीदा काम कर रहे थे, जैसे किसी तरह अपने आप मूल IFS को अंत में ठीक करना (जैसे: पहले चार स्वचालित रूप से बाहर स्थानांतरित या कुछ)।
रस

11

आपके द्वारा प्रदान किया गया कोड समान परिणाम देगा। इसे बेहतर समझने के लिए, इसे आज़माएँ:

foo () {
    for i in "$*"; do
        echo "$i"
    done
}

bar () {
    for i in "$@"; do
        echo "$i"
    done
}

आउटपुट अब अलग होना चाहिए। यहाँ मुझे क्या मिलेगा:

$ foo() 1 2 3 4
1 2 3 4
$ bar() 1 2 3 4
1
2
3
4

इसने मेरे लिए काम किया bash। जहाँ तक मुझे पता है, ksh को ज्यादा अलग नहीं होना चाहिए। अनिवार्य रूप से, उद्धृत $*करने से सब कुछ एक शब्द के रूप में माना जाएगा, और उद्धृत $@करने से सूची को अलग-अलग शब्दों के रूप में माना जाएगा, जैसा कि ऊपर दिए गए उदाहरण में देखा जा सकता है।

IFSसाथ चर का उपयोग करने के एक उदाहरण के रूप में $*, इस पर विचार करें

fooifs () {
    IFS="c"            
    for i in "$*"; do
        echo "$i"
    done
    unset IFS          # reset to the original value
}

मुझे इसका परिणाम मिलता है:

$ fooifs 1 2 3 4
1c2c3c4

इसके अलावा, मैंने अभी पुष्टि की है कि यह उसी में काम करता है ksh। दोनों bashऔर kshयहाँ परीक्षण OSX के अंतर्गत थे, लेकिन मैं यह नहीं देख सकता कि यह कितना मायने रखता है।


"एक समारोह के भीतर बदल गया - वैश्विक को प्रभावित नहीं करता है"। क्या आपने परीक्षण किया?
मिकेल

हां, मैंने वह चेक कर लिया है। मन की शांति के लिए, मैं unset IFSइसे मूल में रीसेट करने के लिए अंत में जोड़ दूंगा , लेकिन इसने मेरे लिए कोई समस्या नहीं की और echo $IFSइसके परिणामस्वरूप मुझे जो मानक आउटपुट मिला, उसे करते हुए। IFSघुंघराले कोष्ठक की स्थापना एक नए दायरे का परिचय देती है, इसलिए जब तक आप इसे निर्यात नहीं करते हैं, यह बाहर को प्रभावित नहीं करेगा IFS
वोजटेक रजेपाल

echo $IFSकुछ भी साबित नहीं होता है, क्योंकि शेल देखता है ,, लेकिन फिर शब्द विभाजन का उपयोग करता है IFS! कोशिश करो echo "$IFS"
मिकेल

अच्छी बात। परेशान IFSहोना चाहिए कि हल।
वोजटेक रजेपाल

जब तक IFSफ़ंक्शन को कॉल करने से पहले एक अलग कस्टम मूल्य नहीं था। लेकिन हां, ज्यादातर समय IFS को परेशान करने से काम चलेगा।
मिकेल

6

अंतर यह महत्वपूर्ण है कि स्क्रिप्ट लिखते समय सही तरीके से स्थितीय मापदंडों का उपयोग करना चाहिए ...

निम्नलिखित कॉल की कल्पना करें:

$ myuseradd -m -c "Carlos Campderrós" ccampderros

यहाँ सिर्फ 4 पैरामीटर हैं:

$1 => -m
$2 => -c
$3 => Carlos Campderrós
$4 => ccampderros

मेरे मामले में, myuseraddकेवल useraddउसी के लिए एक आवरण है जो समान मापदंडों को स्वीकार करता है, लेकिन उपयोगकर्ता के लिए एक कोटा जोड़ता है:

#!/bin/bash -e

useradd "$@"
setquota -u "${!#}" 10000 11000 1000 1100

उद्धृत के useradd "$@"साथ, कॉल को नोटिस करें $@। यह मापदंडों का सम्मान करेगा और उन्हें जैसे-वे भेजेगा useradd। यदि आप अयोग्य थे $@(या अयोग्य का उपयोग $*करना चाहते हैं), तो useradd में 5 पैरामीटर देखने को मिलेंगे , जैसे कि 3 पैरामीटर जिसमें एक स्पेस दो में विभाजित होगा:

$1 => -m
$2 => -c
$3 => Carlos
$4 => Campderrós
$5 => ccampderros

(और इसके विपरीत, आप का उपयोग करने के लिए गए थे, तो "$*", useradd केवल एक पैरामीटर देखना होगा: -m -c Carlos Campderrós ccampderros)

तो, संक्षेप में, यदि आपको बहु-शब्द मापदंडों का सम्मान करने वाले मापदंडों के साथ काम करने की आवश्यकता है, तो उपयोग करें "$@"


4
   *      Expands  to  the positional parameters, starting from one.  When
          the expansion occurs within double quotes, it expands to a  sin
          gle word with the value of each parameter separated by the first
          character of the IFS special variable.  That is, "$*" is equiva
          lent to "$1c$2c...", where c is the first character of the value
          of the IFS variable.  If IFS is unset, the parameters are  sepa
          rated  by  spaces.   If  IFS  is null, the parameters are joined
          without intervening separators.
   @      Expands to the positional parameters, starting from  one.   When
          the  expansion  occurs  within  double  quotes,  each  parameter
          expands to a separate word.  That is, "$@" is equivalent to "$1"
          "$2"  ...   If the double-quoted expansion occurs within a word,
          the expansion of the first parameter is joined with  the  begin
          ning  part  of  the original word, and the expansion of the last
          parameter is joined with the last part  of  the  original  word.
          When  there  are no positional parameters, "$@" and $@ expand to
          nothing (i.e., they are removed).

// आदमी बैश । ksh, afair, समान व्यवहार है।


2

zshऔर बीच के अंतर के बारे में बात करना bash:

चारों ओर उद्धरण के साथ $@और $*, zshऔर bashएक ही व्यवहार करते हैं, और मुझे लगता है कि परिणाम सभी के गोले के बीच काफी मानक है:

 $ f () { for i in "$@"; do echo +"$i"+; done; }; f 'a a' 'b' ''
 +a a+
 +b+
 ++
 $ f () { for i in "$*"; do echo +"$i"+; done; }; f 'a a' 'b' ''
 +a a b +

उद्धरण के बिना, परिणाम के लिए $*और $@, लेकिन में bashऔर में अलग समान हैं zsh। इस मामले में zshकुछ अजीब व्यवहार दिखाता है:

bash$ f () { for i in $*; do echo +"$i"+; done; }; f 'a a' 'b' ''
+a+
+a+
+b+
zsh% f () { for i in $*; do echo +"$i"+; done; }; f 'a a' 'b' ''  
+a a+
+b+

(Zsh आमतौर पर IFS का उपयोग करके टेक्स्ट डेटा को विभाजित नहीं करता है, जब तक कि स्पष्ट रूप से अनुरोध नहीं किया जाता है, लेकिन ध्यान दें कि यहां खाली तर्क सूची में अप्रत्याशित रूप से गायब है।)


1
Zsh में, $@इस संदर्भ में विशेष नहीं है: अधिकांश एक शब्द पर$x फैलता है , लेकिन खाली चर कुछ भी नहीं (एक खाली शब्द नहीं) तक विस्तारित होता है। खाली या अपरिभाषित के साथ प्रयास करें । print -l a $foo bfoo
गाइल्स

0

उत्तर में से एक कहता है $*(जो मुझे "स्पैट" के रूप में लगता है) शायद ही कभी उपयोगी होता है।

मैं के साथ गूगल खोज G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }

चूंकि URL अक्सर एक के साथ विभाजित होते हैं +, लेकिन मेरा कीबोर्ड पहुंच से अधिक आसान बनाता   है +, $*+ $IFSसार्थक महसूस करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.