변수를 확장하려면 큰따옴표가 필요하다는 것을 읽었습니다.
if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi
예상대로 작동하지만
if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi
$test ok
null 인 경우에도 항상 호출됩니다 $test
.
그런데 왜 따옴표가 필요하지 않습니까 echo $test
?
답변1
모든 변수에는 항상 따옴표가 필요합니다.목록즉, 변수를 인용되지 않은 상태로 두는 데 따른 3가지 부작용을 실제로 원하지 않는 한 변수는 어디에서나 여러 값으로 확장될 수 있습니다.
목록컨텍스트에는 [
또는 echo
, for i in <here>
배열에 대한 할당과 같은 간단한 명령의 매개 변수가 포함됩니다. 다른 컨텍스트도 있으며 변수도 참조해야 합니다. 타당한 이유가 없는 한 항상 변수를 인용하는 것이 가장 좋습니다.
고려하면따옴표 없음(목록 컨텍스트에서) 다음과 같이분할+전역운영자.
마치 .echo $test
echo glob(split("$test"))
puts("foo")
대부분의 다른 언어에서는 변수(예: ) 주위가 아닌 고정 문자열 주위에 따옴표를 붙이는 puts(var)
반면, 셸에서는 그 반대입니다. 즉, 셸의 모든 것은 모두 문자열입니다. 따라서 모든 것에 따옴표를 붙이는 것은 번거로울 수 있으며, 여러분은 echo test
그럴 필요가 없습니다 "echo" "test"
. 셸에서 따옴표는 다른 목적, 즉 특정 문자의 특별한 의미를 방지하거나 특정 확장자의 동작에 영향을 주기 위해 사용됩니다.
[ -n $test ]
or 에서 echo $test
쉘은 분할 $test
(기본적으로 공백)을 수행한 다음 파일 이름 생성(모든 *
'?'... 패턴을 일치하는 파일 목록으로 확장)을 수행한 다음 해당 인수 목록을 [
or echo
명령에 전달합니다.
다시 한번 생각해 보세요 "[" "-n" glob(split("$test")) "]"
. 비어 있거나 공백(spc, tab, nl)만 포함하는 경우 $test
분할+glob 연산자는 빈 목록을 반환하므로 "-n"이 빈 문자열인지 확인하는 테스트인 가 [ -n $test ]
됩니다 . "[" "-n" "]"
하지만 $test
"*" 또는 "= foo"라면 어떤 일이 일어날지 상상해 보세요...
, 및 (따옴표 없이) 에는 4개의 매개변수 가 전달되는데 [ -n "$test" ]
, 이는 우리가 원하는 것입니다.[
"["
"-n"
""
"]"
echo
전달 여부는 [
아무런 차이가 없습니다 echo
. 단지 빈 매개변수를 전달하든 매개변수를 전혀 전달하지 않든 동일한 내용이 출력된다는 점만 다를 뿐입니다.
당신은 또한 볼 수 있습니다이 비슷한 질문에 대한 대답[
명령 및 구성 에 대한 [[...]]
자세한 내용 .
답변2
@h3rrmiller의 답변if
따옴표 (또는 오히려 [
/ ) 가 필요한 이유를 설명하는 데 도움이 되지만 test
실제로는 귀하의 질문이 잘못된 것 같습니다.
다음 명령을 시도해 보면 무슨 뜻인지 알 수 있을 것입니다.
export testvar="123 456"
echo $testvar
echo "$testvar"
따옴표가 없으면 변수 대체로 인해 두 번째 명령이 다음으로 확장됩니다.
echo 123 456
그리고 여러 공간이 하나로 축소됩니다.
echo 123 456
따옴표를 사용하면 공백이 유지됩니다.
이런 일이 일어나는 이유는매개변수를 인용하면( 매개변수가 로 전달되거나 다른 명령으로 전달되는지 여부 echo
) test
매개변수의 값이 다음과 같이 전달됩니다.하나명령의 값입니다. 인용하지 않으면 쉘은 각 인수가 시작되고 끝나는 위치를 결정하기 위해 공백을 찾는 일반적인 마법을 수행합니다.
이는 다음의 (매우, 매우 간단한) C 프로그램으로도 설명할 수 있습니다. 명령줄에서 다음을 시도해 보십시오(무언가를 덮어쓸 위험이 없도록 빈 디렉토리에서 이 작업을 수행할 수 있습니다).
cat <<EOF >paramtest.c
#include <stdio.h>
int main(int argc, char **argv) {
int nparams = argc-1; /* because 1 parameter means only the executable's name */
printf("%d parameters received\n", nparams);
return nparams;
}
EOF
cc -o paramtest paramtest.c
그런 다음...
./paramtest 123 456
./paramtest "123 456"
./paramtest 123 456
./paramtest "123 456"
paramtest
를 실행 한 후 $?
전달된 인수의 개수가 저장됩니다(그리고 해당 개수가 인쇄됩니다).
답변3
이것이 쉘이 행의 전체 내용을 해석하는 방법입니다.앞으로프로그램이 실행됩니다.
줄이 로 읽히면 echo I am $USER
쉘은 이를 로 확장 echo I am blrfl
하고 echo
텍스트 소스가 리터럴인지 변수 확장인지 알 수 없습니다. 마찬가지로 줄에 가 포함되어 있으면 echo I am $UNDEFINED
쉘은 $UNDEFINED
아무 것도 확장되지 않고 echo에 대한 인수는 가 되며 I am
그게 끝입니다. echo
매개변수 없이 작동하기 때문에 echo $UNDEFINED
완전히 유효합니다.
귀하의 질문은 if
실제로 문제가 되지 않습니다 if
. 왜냐하면 if
그 뒤에 오는 모든 프로그램과 인수를 실행하고 then
프로그램이 종료될 때 해당 부분을 실행하기 때문입니다 0
(또는 else
프로그램이 종료되지 않는 경우 해당 부분을 실행하십시오 0
).
if /bin/true ; then echo True dat. ; fi
if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi
비교를 수행할 때 if [ ... ]
쉘에 내장된 기본 요소를 사용하지 않습니다 . 기본적으로 이라는 프로그램을 실행하도록 쉘에 지시하는 것입니다. [
이 프로그램은 해당 프로그램의 아주 작은 상위 집합이며 test(1)
마지막 인수는 입니다 ]
. 0
테스트 조건이 참이거나 1
참이 아니면 두 프로그램 모두 종료됩니다.
변수가 정의되지 않았을 때 일부 테스트가 중단되는 이유는 test
변수를 사용하고 있다는 것을 알 수 없기 때문입니다. 따라서 [ $UNDEFINED -eq 2 ]
쉘이 완료할 때 test
표시되는 모든 인수는 -eq 2 ]
유효한 테스트가 아니기 때문에 중단됩니다. 정의된 항목(예: [ $DEFINED -ne 0 ]
)을 사용하여 이 작업을 수행하면 셸이 이를 유효한 테스트(예: )로 확장하므로 작동합니다 0 -ne 0
.
사이에는 의미상 차이가 있으며 , 이름에서 알 수 있듯이 두 개의 매개변수( 및 ) foo $UNDEFINED bar
로 확장됩니다 . 와 비교하면 다음과 같이 확장됩니다.foo
bar
$UNDEFINED
foo "$UNDEFINED" bar
삼매개변수( foo
, 빈 문자열 및 `bar). 따옴표는 셸이 따옴표 사이에 내용이 있는지 여부에 관계없이 따옴표를 인수로 해석하도록 합니다.
답변4
따옴표가 없으면 빈 매개변수가 제거됩니다.
start cmd:> strace -e trace=execve echo foo $bar baz
execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0
start cmd:> strace -e trace=execve echo foo "$bar" baz
execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0
호출된 명령은 쉘 명령줄에 빈 매개변수를 표시하지 않습니다. [는 -n이 다음 항목 없이 0을 반환하는 것으로 정의된 것 같습니다. 어떤 식으로든.
어떤 경우에는 참조가 에코에 영향을 미칩니다.
var='*'
echo $var
echo "$var"
var="foo bar"
echo $var
echo "$var"