지금은 삭제된 답변을 VonC가 다시 작성했습니다;
로버트 갬블의 간결한 답변은 질문에 직접적으로 대응하고 있습니다.
이 답변은 공백이 포함된 파일 이름과 관련된 몇 가지 문제에 대해 자세히 설명합니다.
참조하세요: ${1:+"$@"} in /bin/sh기본 정설:"$@"는 맞고, $*(따옴표로 묶지 않음)는 거의 항상 틀립니다.
이는 인자에 공백이 포함될 때 "$@"가 정상적으로 작동하고
는 공백이 없을 때 $*와 동일하게 작동하기 때문입니다.
어떤 상황에서는 &$*&$$도 괜찮지만 &$@&$$는 보통 (항상 그런 것은 아니지만
항상) 같은 위치에서 작동합니다.
따옴표로 묶지 않은 $@와 $*는 동일합니다(그리고 거의 항상 틀립니다).
그렇다면 $*, $@, &$*&$, &$@&$의 차이점은 무엇일까요? 이들은 모두 셸에 대한 모든 인수와 관련이 있지만 서로 다른 일을 합니다. 따옴표로 묶지 않았을 때 $*와 $@는 같은 일을 합니다. 이들은 각 '단어'(공백이 아닌 시퀀스)를 별도의 인수로 취급합니다. 그러나 따옴표로 묶은 형식은 상당히 다릅니다. &$*"는 인수 목록을 공백으로 구분된 단일 문자열로 취급하는 반면 &$@"는 인수를 명령줄에 지정했을 때와 거의 동일하게 취급합니다.
위치 인수가 없는 경우 "$@"는 전혀 확장되지 않고 "$*"는 빈 문자열 — 로 확장되며, 인식하기 어려울 수 있지만 차이가 있습니다.
자세한 내용은 아래에서 (비표준) 명령 al을 소개한 후 참조하세요.
보조 논문: 공백이 있는 인수를 처리한 다음 다른 명령에
공백으로 인수를 처리해야 하는 경우, 때때로 비표준
도구가 필요할 때가 있습니다. (또는 배열을 신중하게 사용해야 합니다: '"${array[@]}"는 '"$@"와 유사하게 작동합니다.)
예시:
$ mkdir "my dir" anotherdir
$ ls
anotherdir my dir
$ cp /dev/null "my dir/my file"
$ cp /dev/null "anotherdir/myfile"
$ ls -Fltr
total 0
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir/
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir/
$ ls -Fltr *
my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ ls -Fltr "./my dir" "./anotherdir"
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ var='"./my dir" "./anotherdir"' && echo $var
"./my dir" "./anotherdir"
$ ls -Fltr $var
ls: "./anotherdir": No such file or directory
ls: "./my: No such file or directory
ls: dir": No such file or directory
$
왜 작동하지 않나요?
셸이 확장하기 전에 따옴표를 처리하기 때문에 작동하지 않습니다.
변수를 처리하기 때문입니다.
따라서 쉘이 $var에 포함된 따옴표에 주의를 기울이도록 하려면,
eval`을 사용해야 합니다:
$ eval ls -Fltr $var
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$
다음과 같은 파일 이름이 있을 때는 정말 까다로워집니다,
'와 같이 (따옴표와 큰따옴표, 공백으로 묶인) 파일 이름이 있을 때는 까다롭습니다.
$ cp /dev/null "He said, \"Don't do this!\""
$ ls
He said, "Don't do this!" anotherdir my dir
$ ls -l
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 15:54 He said, "Don't do this!"
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir
$
셸은 (모든 셸이) 그런 것들을 처리하기가 특별히 쉽지 않습니다.
처리하기가 쉽지 않기 때문에 (재미있게도) 많은 유닉스 프로그램에서 이러한 셸을
잘 처리하지 못합니다.
유닉스에서 파일 이름(단일 구성 요소)에는 다음을 제외한 모든 문자를 포함할 수 있습니다.
슬래시와 NUL '\0'을 제외한 모든 문자를 포함할 수 있습니다.
그러나 셸에서는 경로 이름에 공백이나 줄바꿈, 탭을
을 사용하지 않는 것이 좋습니다.
표준 Unix 파일 이름에 공백 등이 포함되지 않는 이유이기도 합니다.
공백 및 기타 문제가 될 수 있는 문자를 포함할 수 있는 파일 이름을 다룰 때는
파일 이름을 다룰 때는 매우 조심해야 하며, 저는 오래 전에
오래 전에 유닉스에서 표준이 아닌 프로그램이 필요하다는 것을 알게 되었습니다.
저는 이 프로그램을 escape라고 부릅니다(버전 1.1은 1989-08-23T16:01:45Z 날짜).
다음은 SCCS 제어 시스템에서 사용 중인 escape의 예입니다.
이것은 델타(체크인이라고 생각하세요)와 겟(체크인이라고 생각하세요)을 모두 수행하는 커버 스크립트입니다.
get(*체크아웃*이라고 생각하세요)을 모두 수행하는 커버 스크립트입니다. 다양한 인수, 특히-y(변경한 이유) 에는 공백과 개행이 포함될 수 있습니다. 이 스크립트는 1992년에 작성되었으므로 백 틱 대신에 (cmd ...) 표기법을 사용하며 첫 줄에 #!/bin/sh를 사용하지 않습니다.
: "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
# Delta and get files
# Uses escape to allow for all weird combinations of quotes in arguments
case `basename $0 .sh` in
deledit) eflag="-e";;
esac
sflag="-s"
for arg in "$@"
do
case "$arg" in
-r*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
-e) gargs="$gargs `escape \"$arg\"`"
sflag=""
eflag=""
;;
-*) dargs="$dargs `escape \"$arg\"`"
;;
*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
esac
done
eval delta "$dargs" && eval get $eflag $sflag "$gargs"
(요즘은 이스케이프를 그렇게 철저하게 사용하지 않을 것입니다.
예를 들어 -e 인수와 함께 필요하지 않습니다 - 그러나 전반적으로 이것은
이스케이프를 사용하는 간단한 스크립트 중 하나입니다).
escape프로그램은echo처럼 단순히 인수를 출력합니다. 처럼 단순히 인자를 출력하지만, 인자가 함께 사용하도록 보호됩니다. 평가 (eval의 한 수준; 원격 셸을 실행하는 프로그램이 있는데
실행하고 escape의 출력을 이스케이프해야 하는 프로그램이 있습니다).
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
인수를 한 줄에 하나씩 나열하는 al이라는 또 다른 프로그램이 있습니다.
(그리고 훨씬 더 오래된 버전입니다: 1987-01-27T14:35:49의 버전 1.1).
이 프로그램은 스크립트를 디버깅할 때 가장 유용합니다.
명령줄에 연결하여 명령에 실제로 어떤 인수가 전달되는지 확인할 수 있습니다.
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[추가:
이제 다양한 "$@" 표기법의 차이를 보여드리기 위해 한 가지 예제를 더 살펴보겠습니다:
$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh * */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$
명령줄에서 *와 */* 사이의 원래 공백은 보존되지 않는다는 점에 유의하세요. 또한 다음을 사용하여 셸에서 '명령줄 인수'를 변경할 수 있다는 점에 유의하세요:
set -- -new -opt and "arg with space"
이것은 '-new', '-opt', 'and', 'arg with space'의 4가지 옵션을 설정합니다.
]
흠, 꽤 긴 답변이네요 - 아마도 해석이 더 나은 용어일 것입니다.
요청 시 이스케이프의 소스 코드 제공(이름 닷컴으로 이메일 보내주세요.
성은 gmail 닷컴으로 이메일).
al`의 소스 코드는 매우 간단합니다:
#include
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
그게 다입니다. 이것은 로버트 갬블이 보여준 test.sh 스크립트와 동일하며 셸 함수로 작성할 수 있습니다(하지만 제가 al을 처음 작성할 당시에는 셸 함수가 로컬 버전의 본 셸에 존재하지 않았습니다).
또한 al을 간단한 셸 스크립트로 작성할 수도 있습니다:
[ $# != 0 ] && printf "%s\n" "$@"
인수가 전달되지 않았을 때 출력을 생성하지 않도록 조건문이 필요합니다. printf` 명령은 형식 문자열 인수만 있는 빈 줄을 생성하지만 C 프로그램에서는 아무 것도 생성하지 않습니다.
모든 인수를 나타내려면
"$@"
를 사용합니다:이렇게 하면 각 인수를 반복하여 별도의 줄에 출력합니다. 인용할 때 공백이 있는 경우 인수가 제대로 분리된다는 점을 제외하면 $@는 $*처럼 동작합니다:
지금은 삭제된 답변을 VonC가 다시 작성했습니다; 로버트 갬블의 간결한 답변은 질문에 직접적으로 대응하고 있습니다. 이 답변은 공백이 포함된 파일 이름과 관련된 몇 가지 문제에 대해 자세히 설명합니다. 참조하세요: ${1:+"$@"} in /bin/sh 기본 정설:
"$@"
는 맞고,$*
(따옴표로 묶지 않음)는 거의 항상 틀립니다. 이는 인자에 공백이 포함될 때"$@"
가 정상적으로 작동하고 는 공백이 없을 때$*
와 동일하게 작동하기 때문입니다. 어떤 상황에서는&$*&$$
도 괜찮지만&$@&$$
는 보통 (항상 그런 것은 아니지만 항상) 같은 위치에서 작동합니다. 따옴표로 묶지 않은$@
와$*
는 동일합니다(그리고 거의 항상 틀립니다). 그렇다면$*
,$@
,&$*&$
,&$@&$
의 차이점은 무엇일까요? 이들은 모두 셸에 대한 모든 인수와 관련이 있지만 서로 다른 일을 합니다. 따옴표로 묶지 않았을 때$*
와$@
는 같은 일을 합니다. 이들은 각 '단어'(공백이 아닌 시퀀스)를 별도의 인수로 취급합니다. 그러나 따옴표로 묶은 형식은 상당히 다릅니다.&$*"
는 인수 목록을 공백으로 구분된 단일 문자열로 취급하는 반면&$@"
는 인수를 명령줄에 지정했을 때와 거의 동일하게 취급합니다. 위치 인수가 없는 경우"$@"
는 전혀 확장되지 않고"$*"
는 빈 문자열 — 로 확장되며, 인식하기 어려울 수 있지만 차이가 있습니다. 자세한 내용은 아래에서 (비표준) 명령al
을 소개한 후 참조하세요. 보조 논문: 공백이 있는 인수를 처리한 다음 다른 명령에 공백으로 인수를 처리해야 하는 경우, 때때로 비표준 도구가 필요할 때가 있습니다. (또는 배열을 신중하게 사용해야 합니다: '"${array[@]}"는 '"$@"
와 유사하게 작동합니다.) 예시:왜 작동하지 않나요? 셸이 확장하기 전에 따옴표를 처리하기 때문에 작동하지 않습니다. 변수를 처리하기 때문입니다. 따라서 쉘이
$var
에 포함된 따옴표에 주의를 기울이도록 하려면, eval`을 사용해야 합니다:다음과 같은 파일 이름이 있을 때는 정말 까다로워집니다, '와 같이 (따옴표와 큰따옴표, 공백으로 묶인) 파일 이름이 있을 때는 까다롭습니다.
셸은 (모든 셸이) 그런 것들을 처리하기가 특별히 쉽지 않습니다. 처리하기가 쉽지 않기 때문에 (재미있게도) 많은 유닉스 프로그램에서 이러한 셸을 잘 처리하지 못합니다. 유닉스에서 파일 이름(단일 구성 요소)에는 다음을 제외한 모든 문자를 포함할 수 있습니다. 슬래시와 NUL
'\0'
을 제외한 모든 문자를 포함할 수 있습니다. 그러나 셸에서는 경로 이름에 공백이나 줄바꿈, 탭을 을 사용하지 않는 것이 좋습니다. 표준 Unix 파일 이름에 공백 등이 포함되지 않는 이유이기도 합니다. 공백 및 기타 문제가 될 수 있는 문자를 포함할 수 있는 파일 이름을 다룰 때는 파일 이름을 다룰 때는 매우 조심해야 하며, 저는 오래 전에 오래 전에 유닉스에서 표준이 아닌 프로그램이 필요하다는 것을 알게 되었습니다. 저는 이 프로그램을escape
라고 부릅니다(버전 1.1은 1989-08-23T16:01:45Z 날짜). 다음은 SCCS 제어 시스템에서 사용 중인escape
의 예입니다. 이것은델타
(체크인이라고 생각하세요)와겟
(체크인이라고 생각하세요)을 모두 수행하는 커버 스크립트입니다. get(*체크아웃*이라고 생각하세요)을 모두 수행하는 커버 스크립트입니다. 다양한 인수, 특히
-y(변경한 이유) 에는 공백과 개행이 포함될 수 있습니다. 이 스크립트는 1992년에 작성되었으므로 백 틱 대신에 (cmd ...)
표기법을 사용하며 첫 줄에#!/bin/sh
를 사용하지 않습니다.(요즘은 이스케이프를 그렇게 철저하게 사용하지 않을 것입니다. 예를 들어
-e
인수와 함께 필요하지 않습니다 - 그러나 전반적으로 이것은 이스케이프를 사용하는 간단한 스크립트 중 하나입니다). escape프로그램은
echo처럼 단순히 인수를 출력합니다. 처럼 단순히 인자를 출력하지만, 인자가 함께 사용하도록 보호됩니다. 평가
(eval
의 한 수준; 원격 셸을 실행하는 프로그램이 있는데 실행하고escape
의 출력을 이스케이프해야 하는 프로그램이 있습니다).인수를 한 줄에 하나씩 나열하는
al
이라는 또 다른 프로그램이 있습니다. (그리고 훨씬 더 오래된 버전입니다: 1987-01-27T14:35:49의 버전 1.1). 이 프로그램은 스크립트를 디버깅할 때 가장 유용합니다. 명령줄에 연결하여 명령에 실제로 어떤 인수가 전달되는지 확인할 수 있습니다.[추가: 이제 다양한
"$@"
표기법의 차이를 보여드리기 위해 한 가지 예제를 더 살펴보겠습니다:명령줄에서
*
와*/*
사이의 원래 공백은 보존되지 않는다는 점에 유의하세요. 또한 다음을 사용하여 셸에서 '명령줄 인수'를 변경할 수 있다는 점에 유의하세요:이것은 '
-new
', '-opt
', 'and
', 'arg with space
'의 4가지 옵션을 설정합니다.] 흠, 꽤 긴 답변이네요 - 아마도 해석이 더 나은 용어일 것입니다. 요청 시
이스케이프
의 소스 코드 제공(이름 닷컴으로 이메일 보내주세요. 성은 gmail 닷컴으로 이메일). al`의 소스 코드는 매우 간단합니다:그게 다입니다. 이것은 로버트 갬블이 보여준
test.sh
스크립트와 동일하며 셸 함수로 작성할 수 있습니다(하지만 제가al
을 처음 작성할 당시에는 셸 함수가 로컬 버전의 본 셸에 존재하지 않았습니다). 또한al
을 간단한 셸 스크립트로 작성할 수도 있습니다:인수가 전달되지 않았을 때 출력을 생성하지 않도록 조건문이 필요합니다. printf` 명령은 형식 문자열 인수만 있는 빈 줄을 생성하지만 C 프로그램에서는 아무 것도 생성하지 않습니다.
Robert의 대답이 정답이며
sh
에서도 작동한다는 점에 유의하세요. (휴대용으로) 더 단순화할 수 있습니다:와 같습니다:
즉, 아무것도 필요하지 않습니다!
테스트 (
$
는 명령 프롬프트):저는 커니건과 파이크의 유닉스 프로그래밍 환경에서 이에 대해 처음 읽었습니다.
bash
에서
help for`는 이를 문서화합니다:간단한 경우에 대한 'shift' 을 사용할 수도 있습니다. It 는 인수 목록 같은 대기열은. '각' shift+ctrl 아웃해야 던지는 첫 번째 인수 및 각각의 나머지 색인입니다 인수만 는 감소합니다.
예를 들어 don& 요소이면 어레이에서는 솔리드로 액세스할 수 있습니다 # 39 모두야 반복할 경우 싶지 않다.