다음 코드를 고려해보세요.
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에서 유래하고 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
실행되는 스크립트를 작성해 보겠습니다 . 차이점을 설명하기 위해 세 가지 버전을 만들어 보겠습니다.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"
따라서 "$@"
항상 제대로 작동합니다.
¹ 일부 셸에서는 조심해야 하지만 멀티바이트 문자에서는 작동하지 않습니다.
변수의 유형과 속성을 설정하는 데 사용되는 ²는 4typeset
에서 변수를 로컬로 만듭니다 (ksh93에서는 Bourne 구문이 아닌 Korn 구문으로 정의된 함수에만 해당 ). 이는 함수가 반환될 때 여기가 이전 값으로 복원된다는 의미입니다 . 가 비표준으로 설정되어 있고 일부 확장을 인용하는 것을 잊어버린 경우 나중에 실행하는 명령이 예상대로 작동하지 않을 수 있으므로 이는 중요합니다.ksh
function f {}
f() ...
IFS
IFS
3 첫 번째 항목이 백슬래시로 시작하거나 포함하는 경우 echo
인수를 올바르게 인쇄하거나 인쇄하지 못할 수 있습니다. 백슬래시 처리를 수행하지 않고 (또는 ) 옵션 구분 기호 로 시작하는 인수를 방지하도록 지시할 수 있습니다. 표준 대안이 될 것이지만 ksh88과 pdksh 및 그 파생물 중 일부는 아직 내장되어 있지 않습니다.-
print
-r
-
+
--
-
printf '%s\n' "$*"
printf
4 공백 문자 가 포함되지 않은 "$@"
경우 Bourne 쉘 및 ksh88에서 제대로 작동하지 않았으며 , "인용되지 않은" 공백과 결합된 위치 매개변수 및 분할 $IFS
대상 결과로 효과적으로 구현되었습니다 . Bourne 셸의 초기 버전에는 위치 매개변수가 없을 때 하나의 빈 인수로 확장되는 $IFS
버그가 있었는데 , 이는 때때로 대신에 가 표시되는 이유 중 하나입니다 . 이러한 버그 중 어느 것도 최신 Bourne과 같은 쉘에는 영향을 미치지 않습니다."$@"
${1+"$@"}
"$@"
5 Almquist 껍질을 대신 bosh
사용하세요 . , 또한 ( bash 및 zsh에서도) 별칭이 있으며 에서는 함수에서만 사용할 수 있다는 경고가 있습니다 .local
bash
yash
zsh
typeset
local
declare
bash
local
답변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
)
즉, 다중 단어 매개변수와 관련된 매개변수로 작업해야 하는 경우 를 사용하십시오 "$@"
.