$ *와 $ @의 차이점은 무엇입니까?


72

다음 코드를 고려하십시오.

foo () {
    echo $*
}

bar () {
    echo $@
}

foo 1 2 3 4
bar 1 2 3 4

출력합니다 :

12 34

12 34

Ksh88을 사용하고 있지만 다른 일반적인 쉘에도 관심이 있습니다. 특정 쉘에 대한 특수성을 알고 있다면 언급하십시오.

Solaris의 Ksh 매뉴얼 페이지에서 다음 내용을 발견했습니다.

$ * 및 $ @의 의미는 인용되지 않거나 매개 변수 할당 값 또는 파일 이름으로 사용될 때 동일합니다. 그러나 명령 인수로 사용될 때 $ *는``$ 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( bashash사용 local대신). IFS함수가 반환되면 이전 값으로 복원 됨을 의미 합니다. IFS비표준으로 설정된 경우 이후에 실행하는 명령이 제대로 작동하지 않을 수 있으므로 중요 합니다.


2
훌륭한 설명, 대단히 감사합니다.
rahmu

사용 예를 주셔서 감사합니다 $*. 나는 항상 완전히 쓸모가 없다고 생각했습니다 ... 구분자와의 결합은 좋은 유스 케이스입니다.
anishsane

35

짧은 대답 : 사용하십시오"$@" (큰 따옴표를 유의하십시오). 다른 형태는 거의 유용하지 않습니다.

"$@"다소 이상한 구문입니다. 별도의 필드로서 모든 위치 매개 변수로 대체됩니다. 위치 매개 변수가없는 경우 ( $#0) "$@"비어있는 문자열이 아니라 0 개의 요소가있는 목록으로 확장됩니다. 위치 매개 변수가 하나 있으면 다음 "$@"과 같습니다. "$1"위치 매개 변수가 두 개이면 다음 "$@"과 같습니다. "$1" "$2"

"$@"스크립트 또는 함수의 인수를 다른 명령으로 전달할 수 있습니다. 랩퍼가 호출 된 것과 동일한 인수 및 옵션으로 명령을 호출하기 전에 환경 변수 설정, 데이터 파일 준비 등과 같은 작업을 수행하는 랩퍼에 매우 유용합니다.

예를 들어 다음 함수는의 출력을 필터링합니다 cvs -nq update. 별도로 출력 필터링 및 반환 상태에서 (이다의 grep오히려보다 cvs), 호출 cvssm일부 인수를하는 것은 호출처럼 동작 cvs -nq update이 인수.

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

"$@"위치 매개 변수 목록으로 확장됩니다. 배열을 지원하는 쉘에는 배열의 요소 목록으로 확장하는 비슷한 구문이 있습니다 "${array[@]}"(중괄호는 zsh를 제외하고 필수 임). 다시 한 번 큰 따옴표는 다소 오해의 소지가 있습니다. 배열 요소의 필드 분할 및 패턴 생성을 방지하지만 각 배열 요소는 자체 필드로 끝납니다.

일부 고대 껍질에는 논란의 여지가있는 버그가있었습니다. 위치 인수가 없을 때 "$@"필드가 아닌 빈 문자열을 포함하는 단일 필드로 확장되었습니다. 이로 인해 해결 방법${1+"$@"}생겼습니다 ( Perl 설명서를 통해 유명 함 ). 실제 Bourne 쉘의 이전 버전과 OSF1 구현에만 영향을 미치며 최신 호환 대체품 (ash, ksh, bash 등)은 영향을받지 않습니다. /bin/sh내가 아는 21 세기에 출시 된 시스템에는 영향을 미치지 않습니다 (Tru64 유지 관리 릴리스를 계산하지 않고 /usr/xpg4/bin/sh안전한 경우에도 PATH가 POSIX 호환을 위해 설정 되어있는 한 #!/bin/sh스크립트가 아닌 #!/usr/bin/env sh스크립트 만 영향을 받음 ) . 간단히 말해 이것은 걱정할 필요가없는 역사적 일화입니다.


"$*"항상 한 단어로 확장됩니다. 이 단어는 사이에 공백으로 연결된 위치 매개 변수를 포함합니다. (일반적으로 구분 기호는 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

@Russ :의 첫 번째 문자와 value가 어떻게 연결되는지 보여줍니다 IFS.
cuonglm

같은 방식으로 IFS="^xxxxx"? 후행 ${IFS}접미사로 인해 마지막에 원본 IFS를 자동으로 복구하는 것과 같이 더 까다로운 작업을하고 있다고 생각했습니다 (예 : 첫 번째 문자가 자동으로 이동 또는 기타).
Russ

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되고 모두 kshOSX에서 테스트되었지만 그게 얼마나 중요한지 알 수 없습니다.


"함수 내에서 변경-전역에 영향을 미치지 않습니다". 당신은 그것을 테스트 했습니까?
Mikel

예, 확인했습니다. 마음의 평화 unset IFS를 위해 마지막에 원래대로 재설정하기 위해을 추가 할 것입니다 . 그러나 아무런 문제가 없었 echo $IFS으며 표준 출력을 얻었습니다. IFS중괄호를 사용 하여 설정하면 새로운 범위가 도입되므로 내 보내지 않으면 외부에 영향을 미치지 않습니다 IFS.
Wojtek Rzepala

echo $IFS쉘은을보고 ,, 그러나 단어를 사용하여 분리 하기 때문에 아무것도 증명하지 않습니다 IFS! 시도하십시오 echo "$IFS".
Mikel

좋은 지적. 설정을 해제 IFS하면 해결됩니다.
Wojtek Rzepala 2016 년

IFS함수를 호출하기 전에 다른 사용자 정의 값이 없는 한 . 그러나 그렇습니다. IFS 설정을 해제하면 대부분 작동합니다.
Mikel

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).

// man bash . ksh, afair, 비슷한 행동입니다.


2

사이의 차이점에 대해 이야기 zsh하고 bash:

따옴표로 $@하고 $*, zshbash동일하게 동작하고, 나는 결과가 모든 쉘 사이에 상당히 표준 것 같다 :

 $ 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비어 있거나 정의되지 않은.
Gilles

0

대답 중 하나는 $*( "스 플랫"이라고 생각하는) 거의 유용하지 않다고 말합니다 .

나는 구글 검색 G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }

URL의 자주와 분리되어 있기 때문에 +,하지만 내 키보드를 만드는   것보다 도달하기 쉽게 +, $*+ $IFS보람 느낀다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.