다음 코드를 고려해보세요.
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
변수의 유형과 속성을 설정하고 ksh
4 에서 변수를 지역 변수로 만드는 데 사용됩니다 (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 껍질과 bosh
그 local
대안. bash
, 별칭 ( bash 및 zsh에도 있음) yash
도 zsh
있지만 함수에서만 사용할 수 있다는 점 에 유의해야 합니다 .typeset
local
declare
bash
local
답변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 sh
PATH가 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
.)
즉, 여러 단어로 구성된 매개변수가 포함된 매개변수를 사용해야 한다면 를 사용하세요 "$@"
.