검색 엔진의 방문이 늘어나고 있군...

Posted
Filed under 쉘 스크립트
참조 원문 : Single vs. Double Quotes in Bash

  배시 쉘을 쓰다보면 백 쿼트(`), 싱글 쿼트('), 더블 쿼트(")의 기능이 햇갈리기 마련입니다. 특히 싱글 쿼트와 더블 쿼트는 똑같이 문자열을 처리하기 위해 존재하지만 둘 사이에는 작은 차이가 있고 그 작은 차이도 파고들면 복잡해집니다.

  백 쿼트(`)는 그 안에 있는 명령어의 실행 결과(정확히는 표준 출력-stdout-)를 다시 입력으로 바꾸는 기능을 가지고 있습니다. 주로 변수에 어떤 명령어의 출력 결과를 집어넣을 때 사용합니다. 말로 설명하면 어렵지만 아래의 예로 쉽게 이해할 수 있으리라 생각합니다.
mirashi@myservlab:~$ echo Today is `date +%A`
Today is Tuesday

mirashi@myservlab:~$ uname
Linux
mirashi@myservlab:~$ UNAME_RET=`uname`
mirashi@myservlab:~$ echo $UNAME_RET
Linux

  이제 싱글 쿼트(')에 대해 알아보겠습니다. 두 싱글 쿼트 사이에 있는 문자열은 어떤 해석도 거치지 않고 문자 그대로 인식합니다. 아래의 예를 봅시다.
mirashi@myservlab:~$ name="Juliet Kemp"
mirashi@myservlab:~$ echo "$name"
Juliet Kemp
mirashi@myservlab:~$ echo '$name'
$name
  이렇듯 싱글 쿼트는 변환이고 해석이고 뭐고 가차 없이 문자열을 있는 그대로 받아들입니다. 참 솔직한 녀석이 아닐 수 없습니다. 그러므로 문자열에 특수 문자가 많을 때 유용합니다. 물론 같은 이유로 변수를 다루는 부분에서 쓰면 영 좋지 않겠죠?


  더블 쿼트(")는 좀 더 유연한 녀석입니다. 안에 있는 문자열에서 몇 가지 특수 문자를 재해석합니다. 재해석하는 문자로는 공식적으로 달러($), 백쿼트(`), 그리고 이스케이프(\ 로 시작) 문자가 있습니다만...디폴트 상태의 배시 쉘이라면 여기에 느낌표(!)가 추가됩니다. 여기에 대한 이야기는 조금 있다가 하도록 하겠습니다.

  더블 쿼트는 문자열 중간에 공백이 있을 때, 변수와 함께 사용할 때, 명령어의 인수로 사용할 문자열에 공백 문자가 포함되어 하나의 인수가 둘로 인식되는 것을 막을 때 유용합니다. 셋 중 앞의 2개는 바로 위에 있는 싱글 쿼트 예제의 첫 번째 줄과 두 번째 줄에서 차례대로 볼 수 있습니다. 아래는 세 번째 내용을 설명하기 위한 예제입니다.
mirashi@myservlab:~$ mkdir my directory
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
mirashi@myservlab:~$ mkdir "my directory"
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my directory
  전자의 경우 my와 directory라는 2개의 디렉토리가 만들어졌지만 후자는 공백 문자가 포함된 my directory라는 디렉토리가 만들어졌습니다. 그렇다면 이번에는 변수와 함께 사용할 때 어떻게 유용한지 볼까요?
mirashi@myservlab:~$ NameToRemove="my directory"
mirashi@myservlab:~$ rmdir "$NameToRemove"
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
mirashi@myservlab:~$ rmdir $NameToRemove
mirashi@myservlab:~$ ls -l
mirashi@myservlab:~$
  둘의 차이가 보이십니까? 변수를 인수로 사용할 때 변수명을 더블 쿼트로 감싸면 변수의 내용 전체가 하나의 인수로 인식되는 것을 볼 수 있습니다. 변수의 내용을 변수명 대신 그대로 대입해보면 위 내용을 쉽게 이해할 수 있으리라 생각합니다. 또 다른 예로 아래와 같은 경우가 있습니다.
mirashi@myservlab:~$ ls
abcdef
mirashi@myservlab:~$ dpkg-query -l abc*
No packages found matching abcdef.
mirashi@myservlab:~$ dpkg-query -l "abc*"
No packages found matching abc*.
  사용자가 있는 디렉토리에는 abcdef라는 이름의 파일이 있습니다. 이 상태에서 abc라는 이름으로 시작하는 패키지를 검색하기 위해 dpkg-query -l abc* 라고 명령어를 실행했습니다. 그랬더니 abc*가 확장되어 abcdef로 변한 다음 dpkg-query 명령어가 실행되는 것을 볼 수 있습니다. 그래서 abc*를 더블 쿼트로 묶어 실행했더니 의도대로 명령어가 실행되는 것을 볼 수 있습니다.


  아까 더블 쿼트에서 달러($), 백쿼트(`), 그리고 이스케이프 문자 외에 느낌표(!)도 재해석되는 문자에 포함된다고 했는데 사실 엄밀히 따지자면 더블 쿼트와는 관계가 없습니다. 배시 쉘에서 느낌표는 '히스토리 익스팬션(History Expansion)'이라는 것의 디폴트 문자로 쓰이는데 이것은 배시 쉘의 히스토리 기능을 이용한 일종의 명령어 단축 실행 기능이라고 할 수 있습니다. 대충 아래와 같은 기능입니다.
mirashi@myservlab:~$ echo "history expansion"
history expansion
mirashi@myservlab:~$ !e
echo "history expansion"
history expansion
  문제는 이 히스토리 익스팬션 문자라는 것은 백슬래시(\)와 싱글 쿼트(')로만 문자 자체로서 표시(Quote)할 수 있다는 겁니다. 그럼 혹자는 '느낌표 앞에 백슬래시를 쓰면 되겠네?'라고 생각하겠지만 아래처럼 그 방법은 안 통합니다.
mirashi@myservlab:~$ echo "hi\!"
hi\!
  느낌표가 히스토리 익스팬션 문자로 쓰이는 것은 막았으나 더블 쿼트에서 재해석되는 문자는 달러($), 백쿼트(`), 이스케이프 문자 밖에 없기 때문에 백슬래시도 그대로 출력되어 버립니다. 그러면 느낌표를 어떻게 해야 출력할 수 있을까요? 혹자는 아래와 같은 해결 방안을 제시할 수도 있습니다.
mirashi@myservlab:~$ echo hi!
hi!
  네. 되긴 됩니다. 근데 쿼트를 아예 안 쓰고 억지고 해결했다는 문제는 제끼더라도 저게 과연 제대로 작동하고 있는 걸까요? 아래를 봅시다.
mirashi@myservlab:~$ echo hi!yo
-bash: !yo: event not found
  결국 눈가리고 아웅이라는 겁니다. 출력은 됐을지언정 그 출력된 느낌표는 여전히 히스토리 익스팬션 문자로서 역할을 하고 있는 것이죠. 이 문제를 해결하는 방법은 2가지가 있는데 하나는 싱글 쿼트를 사용하는 겁니다. 이 포스트에서 가장 먼저 설명했듯이 싱글 쿼트는 다음 싱글 쿼트가 나오기 전까지의 모든 문자를 문자 그대로 받아들입니다. 조금 전에도 히스토리 익스팬션 문자는 백슬래시(\)와 싱글 쿼트(')로만 문자 자체로서 표시할 수 있다고 했었지요.
mirashi@myservlab:~$ echo 'hi!'
hi!
  다른 방법은 히스토리 익스팬션 문자를 다른 것으로 바꾸거나 아예 없애버리는 겁니다. 히스토리 익스팬션 문자는 'histchars'이라는 이름의 쉘 변수를 통해 지정할 수 있습니다. 참고로 오타 아니고 정말 소문자 맞습니다. 아래처럼 저 변수에 빈 값을 넣으면 자연스럽게 이 기능을 사용하지 않게 되는 것이죠.
mirashi@myservlab:~$ histchars=
mirashi@myservlab:~$ echo "hi!"
hi!
  그런데 배시 쉘은 디폴트로 느낌표를 히스토리 익스팬션 문자로 사용하기 때문에 이 방법은 별로 좋은 방법이라고 할 수 없겠습니다. 자기 호스트에서 저렇게 설정하고 저걸 기준으로 스크립트를 짰다가 다른 호스트에 복사해서 그대로 사용했다간...별로 좋지 못하겠죠. 히스토리 익스팬션은 정말 뻘스러운 기능이 아닌가 싶습니다. 마지막으로 원문의 리플에 달린 뻘스럽지만 그래서 좋은 예제를 보며 포스트를 마치겠습니다.
bash$ cost=2.79
bash$ echo "Alice's sister said, \"Gas costs \$$cost/gal"'!'"\""
Alice's sister said, "Gas costs $2.79/gal!"
bash$ echo 'Bob'"'"'s reply was, "$'"$cost"' is too much!"'
Bob's reply was, "$2.79 is too much!"


2010/10/21 18:00 2010/10/21 18:00