$*와 $@의 차이점은 무엇인가요?

$*와 $@의 차이점은 무엇인가요?

다음 코드를 고려해보세요.

foo () {
    echo $*
}

bar () {
    echo $@
}

foo 1 2 3 4
bar 1 2 3 4

다음과 같이 출력됩니다.

1 2 3 4

1 2 3 4

Ksh88을 사용하고 있지만 다른 일반적인 쉘에도 관심이 있습니다. 특정 쉘의 특징을 알고 계시다면 꼭 언급해주세요.

Solaris의 Ksh 매뉴얼 페이지에서 다음을 찾았습니다.

$*와 $@는 인용하지 않거나 매개변수 할당값 또는 파일명으로 사용하는 경우에는 의미가 동일합니다. 그러나 명령 인수로 사용될 때 $*는 ``$1d$2d...''와 동일합니다. 여기서 d는 IFS 변수의 첫 번째 문자인 반면 $@는 $1 $2 ...와 동일합니다.

변수 수정을 시도했지만 IFS출력이 수정되지 않습니다. 어쩌면 내가 뭔가 잘못하고 있는 건 아닐까?

답변1

인용되지 않은 경우 $*$@동일합니다. 공백이나 와일드카드가 포함된 인수가 있는 경우 예기치 않게 중단될 수 있으므로 이들 중 하나를 사용하면 안 됩니다.


"$*"한 단어로 확장됩니다 "$1c$2c...". cBourne 셸에서는 공백이었지만 이제는 IFS현대 Bourne 유사 셸(ksh에서 유래하고 sh에 대해 POSIX에서 지정됨)의 첫 번째 문자이므로 원하는 대로 무엇이든 될 수 있습니다.

내가 찾은 유일한 좋은 용도는 다음과 같습니다.

쉼표로 인수 결합(간단한 버전)

function join1 {
    typeset IFS=,      # typeset makes a local variable in ksh²
    print -r -- "$*"   # using print instead of unreliable echo³
}

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

인수를 지정된 구분 기호로 결합(더 나은 버전)

function join2 {
    typeset IFS="$1"
    shift
    print -r -- "$*"
}

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

"$@"별도의 단어로 확장됩니다."$1" "$2" ...

이것은 거의 항상 당신이 원하는 것입니다. 각 위치 매개변수를 별도의 단어로 확장하므로 명령줄이나 함수 인수를 가져와 다른 명령이나 함수에 전달하는 데 적합합니다. 그리고 큰따옴표를 사용하여 확장하기 때문에 "$1"공백이나 별표( *) 4 가 포함되어 있어도 문제가 발생하지 않는다는 의미입니다 .


으로 svim실행되는 스크립트를 작성해 보겠습니다 . 차이점을 설명하기 위해 세 가지 버전을 만들어 보겠습니다.vimsudo

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"

따라서 "$@"항상 제대로 작동합니다.


¹ 일부 셸에서는 조심해야 하지만 멀티바이트 문자에서는 작동하지 않습니다.

변수의 유형과 속성을 설정하는 데 사용되는 ²는 4typeset 에서 변수를 로컬로 만듭니다 (ksh93에서는 Bourne 구문이 아닌 Korn 구문으로 정의된 함수에만 해당 ). 이는 함수가 반환될 때 여기가 이전 값으로 복원된다는 의미입니다 . 가 비표준으로 설정되어 있고 일부 확장을 인용하는 것을 잊어버린 경우 나중에 실행하는 명령이 예상대로 작동하지 않을 수 있으므로 이는 중요합니다.kshfunction f {}f() ...IFSIFS

3 첫 번째 항목이 백슬래시로 시작하거나 포함하는 경우 echo인수를 올바르게 인쇄하거나 인쇄하지 못할 수 있습니다. 백슬래시 처리를 수행하지 않고 (또는 ) 옵션 구분 기호 로 시작하는 인수를 방지하도록 지시할 수 있습니다. 표준 대안이 될 것이지만 ksh88과 pdksh 및 그 파생물 중 일부는 아직 내장되어 있지 않습니다.-print-r-+---printf '%s\n' "$*"printf

4 공백 문자 가 포함되지 않은 "$@"경우 Bourne 쉘 및 ksh88에서 제대로 작동하지 않았으며 , "인용되지 않은" 공백과 결합된 위치 매개변수 및 분할 $IFS대상 결과로 효과적으로 구현되었습니다 . Bourne 셸의 초기 버전에는 위치 매개변수가 없을 때 하나의 빈 인수로 확장되는 $IFS버그가 있었는데 , 이는 때때로 대신에 가 표시되는 이유 중 하나입니다 . 이러한 버그 중 어느 것도 최신 Bourne과 같은 쉘에는 영향을 미치지 않습니다."$@"${1+"$@"}"$@"

5 Almquist 껍질을 대신 bosh사용하세요 . , 또한 ( bash 및 zsh에서도) 별칭이 있으며 에서는 함수에서만 사용할 수 있다는 경고가 있습니다 .localbashyashzshtypesetlocaldeclarebashlocal

답변2

짧은 답변:사용"$@"(큰따옴표에 주의하세요) 다른 형식은 거의 유용하지 않습니다.

"$@"다소 이상한 구문입니다. 이는 별도의 필드로서 모든 위치 매개변수로 대체됩니다. 위치 매개변수가 없는 경우( $#is 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및 3개의 파일이 포함된 경우 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`

답변3

$*다음은 과 의 차이점을 보여주는 간단한 스크립트입니다 $@.

#!/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<==

$*배열 구문에서는 or 를 사용해도 차이가 없습니다 $@. 큰따옴표 "$*"및 와 함께 사용할 때만 의미가 있습니다 "$@".

답변4

위치 매개변수를 올바른 방식으로 사용해야 하는 스크립트를 작성할 때 차이점이 중요합니다.

다음 호출을 상상해 보세요.

$ 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공백이 포함된 세 번째 매개변수는 두 개로 분할됩니다.

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

(반대로 를 사용하는 경우 "$*"useradd 는 하나의 매개변수만 볼 수 있습니다: -m -c Carlos Campderrós ccampderros)

즉, 다중 단어 매개변수와 관련된 매개변수로 작업해야 하는 경우 를 사용하십시오 "$@".

관련 정보