it-swarm-eu.dev

Jaký je rozdíl mezi $ * a $ @?

Zvažte následující kód:

foo () {
    echo $*
}

bar () {
    echo [email protected]
}

foo 1 2 3 4
bar 1 2 3 4

Výstupy:

1 2 3 4

1 2 3 4

Používám Ksh88, ale zajímají mě i další běžné skořápky. Pokud náhodou znáte specifičnost konkrétních skořápek, uveďte je prosím.

Follwing jsem našel na mužské stránce Ksh man na Solarisu):

Význam $ * a $ @ je identický, pokud není uveden, nebo když je použit jako hodnota přiřazení parametru nebo jako název souboru. Když se však použije jako argument příkazu, $ * odpovídá `` $ 1d $ 2d ... '', kde d je první znak proměnné IFS, zatímco $ @ je ekvivalentní $ 1 $ 2 ....

Pokusil jsem se upravit proměnnou IFS, ale nemění to výstup. Možná dělám něco špatného?

92
rahmu

Pokud nejsou citovány, $* a [email protected] jsou stejní. Neměli byste používat ani jeden z nich, protože se mohou neočekávaně zlomit, jakmile máte argumenty obsahující mezery nebo zástupné znaky.


"$*" se rozšíří na jediné slovo "$1c$2c...". Obvykle c je mezera, ale ve skutečnosti je to první znak IFS, takže to může být cokoli, co si vyberete.

Jediné dobré použití, jaké jsem kdy našel, je:

spojit argumenty s čárko (jednoduchá verze)

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

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

spojit argumenty se zadaným oddělovačem (lepší verze)

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

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

"[email protected]" rozbalí na samostatná slova: "$1""$2"...

To je téměř vždy to, co chcete. Rozšiřuje každý polohový parametr do samostatného slova, což je ideální pro převzetí argumentů příkazového řádku nebo funkce a jejich předání jinému příkazu nebo funkci. A protože se rozšiřuje pomocí dvojitých uvozovek, znamená to, že se věci nerozbijí, pokud řekněme, "$1" obsahuje mezeru nebo hvězdičku (*).


Napíšeme skript s názvem svim, který spustí vim s Sudo. Uděláme tři verze, abychom ilustrovali rozdíl.

svim1

#!/bin/sh
Sudo vim $*

svim2

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

svim3

#!/bin/sh
Sudo vim "[email protected]"

Všechny z nich budou v pořádku pro jednoduché případy, např. jeden název souboru, který neobsahuje mezery:

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

Ale pouze $* a "[email protected]" funguje správně, pokud máte více argumentů.

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"

A jenom "$*" a "[email protected]" funguje správně, pokud máte argumenty obsahující mezery.

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"

Takže pouze "[email protected]" bude vždy fungovat správně.


typeset je, jak vytvořit lokální proměnnou v ksh (bash a ash místo toho použijte local). To znamená, že IFS bude obnovena na předchozí hodnotu, jakmile se funkce vrátí. To je důležité, protože příkazy, které později spustíte, nemusí fungovat správně, pokud je položka IFS nastavena na něco nestandardního.

114
Mikel

Krátká odpověď: použijte "[email protected]" (všimněte si dvojitých uvozovek). Ostatní formy jsou velmi zřídka užitečné.

"[email protected]" je poněkud podivná syntaxe. Nahrazuje se všemi polohovými parametry jako samostatná pole. Pokud neexistují žádné poziční parametry ($# je 0), potom "[email protected]" expanduje na nic (ne prázdný řetězec, ale seznam s 0 prvky), pokud existuje jeden poziční parametr, pak "[email protected]" je ekvivalentní s "$1", pokud existují dva poziční parametry, pak "[email protected]" je ekvivalentní s "$1" "$2", atd.

"[email protected]" vám umožňuje předat argumenty skriptu nebo funkce jinému příkazu. Je velmi užitečné pro obálky, které dělají věci, jako je nastavení proměnných prostředí, příprava datových souborů atd., Předtím, než zavoláte příkaz se stejnými argumenty a možnostmi, s nimiž byl obálka zavolána.

Například následující funkce filtruje výstup cvs -nq update. Kromě filtrování výstupu a návratového stavu (což je stav grep spíše než stav cvs) se volání cvssm u některých argumentů chová jako volání cvs -nq update s těmito argumenty.

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

"[email protected]" se rozšíří na seznam pozičních parametrů. V shellech, které podporují pole, existuje podobná syntaxe pro rozbalení do seznamu prvků pole: "${array[@]}" (rovnátka jsou povinná s výjimkou zsh). Znovu jsou dvojité uvozovky poněkud zavádějící: chrání před rozdělením pole a generováním vzorů prvků pole, ale každý prvek pole končí ve svém vlastním poli.

Některé starověké granáty měly co je pravděpodobně chyba: když neexistovaly žádné poziční argumenty, "[email protected]" rozšířeno na jedno pole obsahující prázdný řetězec, nikoli do žádného pole. To vedlo k řešení ${1+"[email protected]"} (vytvořeno slavné dokumentací Perl ). Ovlivněny jsou pouze starší verze skutečné Bourne Shell a OSF1, žádná z jejích moderních kompatibilních náhrad (popel, ksh, bash,…) není. /bin/sh není ovlivněn na žádném systému, který byl propuštěn v 21. století, o kterém vím (pokud nepočítáte vydání údržby Tru64, a to i tam /usr/xpg4/bin/sh je bezpečné, takže pouze #!/bin/sh skript je ovlivněn, nikoli #!/usr/bin/env sh skripty, pokud je vaše PATH nastavena na kompatibilitu s POSIX). Stručně řečeno, jedná se o historickou anekdotu, kterou si nemusíte dělat starosti.


"$*" se vždy rozšíří na jedno slovo. Toto slovo obsahuje poziční parametry zřetězené mezerou mezi nimi. (Obecněji je oddělovač prvním znakem hodnoty proměnné IFS. Pokud je hodnota IFS prázdný řetězec, oddělovač je prázdný řetězec.) Pokud neexistují žádné polohové parametry pak "$*" je prázdný řetězec, pokud existují dva poziční parametry a IFS má svou výchozí hodnotu, pak "$*" je ekvivalentní s "$1 $2", atd.

[email protected] a $* vnější uvozovky jsou ekvivalentní. Rozbalí se do seznamu pozičních parametrů jako samostatná pole, například "[email protected]"; ale každé výsledné pole je pak rozděleno do samostatných polí, která jsou považována za zástupné vzory názvu souboru, jako obvykle u nekótovaných proměnných rozšíření.

Pokud například aktuální adresář obsahuje tři soubory bar, baz a foo, pak:

set --         # no positional parameters
for x in "[email protected]"; 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 "[email protected]"      # prints `b* c* qux`
echo "$*"      # prints `b* c* qux`
echo $*        # prints `bar baz c* qux`
for x in "[email protected]"; 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`

Zde je jednoduchý skript, který demonstruje rozdíl mezi $* a [email protected]:

#!/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 '[email protected]'
  for param in [email protected]; do
    printf '==>%s<==\n' "$param"
  done;

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

IFS="^${IFS}"

test_param 1 2 3 "a b c"

Výstup:

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

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

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

Using [email protected]
==>1<==
==>2<==
==>3<==
==>a<==
==>b<==
==>c<==

Using "[email protected]"
==>1<==
==>2<==
==>3<==
==>a b c<==

V syntaxi pole není žádný rozdíl při použití $* nebo [email protected]. Dává to smysl, pouze pokud je používáte s uvozovkami "$*" a "[email protected]".

27
cuonglm

Kód, který jste zadali, poskytne stejný výsledek. Chcete-li to lépe pochopit, zkuste toto:

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

bar () {
    for i in "[email protected]"; do
        echo "$i"
    done
}

Výstup by nyní měl být jiný. Tady je to, co dostanu:

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

Toto pro mě fungovalo na bash. Pokud vím, ksh by se neměl příliš lišit. V podstatě citace $* bude považovat vše za jedno slovo a citovat [email protected] bude se seznamem zacházet jako se samostatnými slovy, jak je vidět na příkladu výše.

Jako příklad použití proměnné IFS s $*, zvaž toto

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

Výsledkem je:

$ fooifs 1 2 3 4
1c2c3c4

Také jsem právě potvrdil, že to samé funguje v ksh. Testy bash a ksh testované zde byly pod OSX, ale nevidím, jak by na tom hodně záleželo.

11
Wojtek

Rozdíl je důležitý při psaní skriptů, které by měly správně používat poziční parametry ...

Představte si následující hovor:

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

Zde jsou pouze 4 parametry:

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

V mém případě je myuseradd pouze obal pro useradd, který přijímá stejné parametry, ale přidává kvótu pro uživatele:

#!/bin/bash -e

useradd "[email protected]"
setquota -u "${!#}" 10000 11000 1000 1100

Všimněte si hovoru na useradd "[email protected]" S uvozovkou [email protected]. To bude respektovat parametry a pošle je tak, jak jsou, na useradd. Pokud byste chtěli zrušit citaci [email protected] (Nebo použít $* Také bez uvozovek), uživatel by viděl parametry 5, protože by se rozdělil třetí parametr, který obsahoval mezeru dva:

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

(a naopak, pokud byste měli použít "$*", uživatel by viděl pouze jeden parametr: -m -c Carlos Campderrós ccampderros)

Stručně řečeno, pokud potřebujete pracovat s parametry respektujícími parametry více slov, použijte "[email protected]".

6
   *      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, "[email protected]" 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, "[email protected]" and [email protected] expand to
          nothing (i.e., they are removed).

// man bash . je ksh, afair, podobné chování.

4
rush

Když mluvíme o rozdílech mezi zsh a bash:

S uvozovkami kolem [email protected] a $*, zsh a bash se chovají stejně a myslím, že výsledek je u všech prostředí zcela standardní:

 $ f () { for i in "[email protected]"; 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 +

Bez uvozovek jsou výsledky stejné pro $* a [email protected], ale liší se v bash a zsh. V tomto případě zsh ukazuje některé zvláštní chování:

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 obvykle nerozděluje textová data pomocí IFS, pokud to není výslovně požadováno, ale všimněte si, že zde prázdný argument v seznamu neočekávaně chybí.)

2

Jedna z odpovědí říká, že $* (Což považuji za „ikona“) je zřídka užitečné.

Hledám google s G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }

Protože adresy URL se často dělí s +, Ale moje klávesnice usnadňuje přístup než +, $* + $IFS Se tedy vyplatí.

0
isomorphismes