에코에 대한 것이 아닌 if에 변수를 인용 해야하는 이유는 무엇입니까?


26

변수 확장을 위해 큰 따옴표가 필요하다는 것을 읽었습니다.

if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi

예상대로 작동하지만

if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi

null 인 $test ok경우에도 항상 말할 것입니다 $test.

그런데 왜 우리는 따옴표가 필요하지 echo $test않습니까?


2
에 대한 인수로 사용할 변수를 인용하지 않으면 echo추가 공백과 줄 바꿈이 제거됩니다.
jordanm

답변:


36

모든 목록 컨텍스트 에서 항상 변수 주위에 따옴표가 필요합니다 . 즉, 변수를 따옴표로 묶지 않은 3 가지 부작용을 원하지 않는 한 변수가 여러 값으로 확장 될 수 있습니다.

리스트 컨텍스트에는 [or echo,, for i in <here>배열에 대한 할당 과 같은 간단한 명령에 대한 인수가 포함 됩니다. 변수를 인용해야하는 다른 컨텍스트도 있습니다. 가장 좋은 이유는 그렇지 않은 이유가없는 한 항상 변수를 인용하는 것입니다.

split + glob 연산자 로 따옴표 (목록 컨텍스트) 가 없다고 생각하십시오 .

마치 것은 echo $test이었다 echo glob(split("$test")).

쉘 동작은 대부분의 사람들에게 혼란 스러울 수 있습니다. 대부분의 다른 언어에서는 puts("foo")변수와 같은 고정 되지 않은 문자열 주위에 따옴표를 넣고 puts(var)셸에서는 다른 방법으로 따옴표를 넣습니다. 귀찮을 것입니다 echo test, 당신은, 당신은 필요하지 않습니다 "echo" "test". 셸에서 따옴표는 다른 용도로 사용됩니다. 일부 문자의 특별한 의미를 방지하거나 확장의 동작에 영향을 미칩니다.

에서 [ -n $test ]또는 echo $test, 쉘은 분할합니다 $test(기본적으로 공백에) 한 다음 파일 이름 생성을 수행 (모든 확장 *, '?'... 패턴과 일치하는 파일의 목록), 다음 인수의 목록을 통과 [또는 echo명령 .

다시 한 번 생각하십시오 "[" "-n" glob(split("$test")) "]". 경우 $test비어 있거나 공백 (SPC, 탭, NL)가 포함 된 그래서 다음 분할 + 글로브 연산자는 빈리스트를 반환 [ -n $test ]될 것 "[" "-n" "]""-n"이 빈 문자열인지 처리 여부를 확인하는 시험이다. 그러나 $test"*"또는 "= foo"인 경우 어떻게되었을 지 상상해보십시오 .

에서 [ -n "$test" ], [네 개의 인수를 전달 "[", "-n", ""그리고 "]"우리가 원하는 것을 인 (따옴표없이).

그것의 여부 echo[차이가 없습니다, 그것은 단지의 echo같은 것을 출력 해, 하늘의 인수 또는 전혀 인수를 전달 여부.

명령과 구문 에 대한 자세한 내용 은 유사한 질문에 대한 이 답변을 참조하십시오 .[[[...]]


7

@ 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, $?매개 변수가 전달되었습니다 (그 번호가 인쇄됩니다)의 수를 개최한다.


2

이것은 프로그램이 실행 되기 전에 쉘이 어떻게 라인을 해석하는지에 관한 것 입니다.

라인이 읽는 경우 echo I am $USER, 쉘은 그것을 확장 echo I am blrflecho텍스트의 기원은 리터럴 또는 변수 확장을인지 단서가 없습니다. 마찬가지로, 행이 echo I am $UNDEFINED이면 쉘은 $UNDEFINED아무것도 확장 되지 않고 echo의 인수는 I am입니다. 이것이 끝입니다. echo인수없이 잘 작동 하기 때문에 echo $UNDEFINED완전히 유효합니다.

와 함께하는 문제 if는 실제로 어떤 프로그램도 실행 하지 않고 인수가 뒤 따르고 프로그램이 종료되면 (또는 하나가 있고 프로그램이 아닌 경우 ) 부분을 실행 if하기 때문에 실제로는 아닙니다 .ifthen0else0

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입니다. Ergo, [ $UNDEFINED -eq 2 ]쉘이 완료 될 때까지 test인수가 모두 보이 므로 -eq 2 ]유효한 테스트 가 아니기 때문에 중단됩니다 . 와 같이 정의 된 것으로 수행 한 경우, [ $DEFINED -ne 0 ]쉘이 유효한 테스트 (예 :)로 확장하기 때문에 작동합니다 0 -ne 0.

사이에는 의미 상 차이가 있는데 foo $UNDEFINED bar, 이름에 따라 두 개의 인수 ( foobar)로 확장됩니다 $UNDEFINED. 이것을 세 개의 인수 ( , 빈 문자열 및`bar)로 foo "$UNDEFINED" bar확장하는와 비교하십시오 . 따옴표는 쉘이 그것들 사이에 어떤 것이 있든 없든, 그것들을 인수로 해석하도록 강제합니다.foo


0

따옴표 $test가 없으면 두 단어 이상으로 확장 될 수 있으므로 [명령 내부의 각 스위치 는 따옴표가하는 하나의 인수를 기대하기 때문에 구문을 위반하지 않기 위해 따옴표가 필요합니다 ( $test하나의 인수로 확장되는 것은 무엇이든 )

변수를 확장하기 위해 따옴표가 필요하지 않은 이유 echo는 하나의 인수를 기대하지 않기 때문입니다. 그것은 당신이 말한 것을 단순히 인쇄 할 것입니다. 따라서 $test100 단어로 확장 하더라도 에코는 여전히 인쇄합니다.

Bash Pitfalls 살펴보기


예. 왜 필요하지 echo않습니까?
CharlesB

@CharlesB에 대한 따옴표가 필요합니다 echo. 당신이 다르게 생각하게 만드는 것은 무엇입니까?
Gilles 'SO- 악마 그만해'

나는 그것들이 필요없고, 가능 echo $test하고 작동합니다 ($ test 값을 출력합니다)
CharlesB

1
@CharlesB $ test의 값이 어디에도 여러 공간을 포함하지 않는 경우에만 출력합니다. 이유를 설명하기 위해 대답에서 프로그램을 사용해보십시오.
CVn

0

인용되지 않은 경우 빈 매개 변수가 제거됩니다.

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"

2
echo셸 이 아닙니다 . 와 동일한 동작을 볼 수 ls있습니다. 시도 touch '*'는 모험을 경험하는 경우에 약간의 시간을. :)
CVn

'if [...]`경우와 차이가 없기 때문에 말 그대로입니다. [는 특별한 쉘 명령이 아닙니다. 인용이 필요하지 않은 [[(bash에서)와 다릅니다.
Hauke ​​Laging 2013
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.