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

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

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

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...". c는 Bourne 셸의 공백이지만 이제 IFS현대 Bourne 유사 셸(ksh에서 유래하고 POSIX에서 sh로 지정)의 첫 번째 문자이므로 원하는 대로 지정할 수 있습니다.

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

매개변수를 쉼표로 연결(간단한 버전)

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만들어 보겠습니다 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변수의 유형과 속성을 설정하고 ksh4 에서 변수를 지역 변수로 만드는 데 사용됩니다 (ksh93에서는 function f {}Bourne 구문을 사용하여 정의된 함수가 아니라 Korn 구문을 f() ...사용하여 정의된 함수에만 적용됩니다). 즉 IFS, 함수가 반환되면 이전 값이 복원됩니다. IFS설정이 표준이 아니고 일부 확장명을 인용하는 것을 잊은 경우 나중에 실행하는 명령이 예상대로 작동하지 않을 수 있으므로 이는 중요합니다 .

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

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

5 Almquist 껍질과 boshlocal대안. bash, 별칭 ( bash 및 zsh에도 있음) yashzsh있지만 함수에서만 사용할 수 있다는 점 에 유의해야 합니다 .typesetlocaldeclarebashlocal

답변2

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

"$@"다소 이상한 구문입니다. 이는 별도의 필드인 모든 위치 매개변수로 대체됩니다. 위치 인수가 없는 경우( $#is 0) "$@"아무것도 없는 것으로 확장됩니다(빈 문자열이 아니라 요소가 0인 목록). 이는 위치 인수가 1개 있는 경우와 "$@"동일 하거나 "$1"위치 인수가 두 개 있는 경우 ETC "$@"와 동일합니다 . "$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보안 릴리스 도 있으므로 #!/bin/sh스크립트만 영향을 받으며 #!/usr/bin/env shPATH가 POSIX 준수로 설정되어 있는 한 스크립트는 영향을 받지 않습니다.) 어쨌든 걱정할 필요가 없는 역사적 일화가 있습니다.


"$*"항상 단어로 확장됩니다. 이 단어에는 공백으로 연결된 위치 매개변수가 포함되어 있습니다. (보다 일반적으로 구분 기호는 변수 value 의 첫 번째 문자입니다 IFS. 값이 IFS빈 문자열인 경우 구분 기호는 빈 문자열입니다.) 구분 기호는 "$*"위치 인수가 없는 경우 빈 문자열이고, 두 개의 위치 인수가 있으며 IFS기본값을 가지며 etc. "$*"와 동일합니다 ."$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.)

즉, 여러 단어로 구성된 매개변수가 포함된 매개변수를 사용해야 한다면 를 사용하세요 "$@".

관련 정보