6장. 쿼우팅(quoting)

쿼우팅이란 문자열을 따옴표로 묶는 것을 말합니다. 이렇게 하는 이유는 문자열 안에 특수 문자가 들어가 있을 경우, 쉘이나 쉘 스크립트에 의해 그 특수 문자가 재해석이나 확장되는 것을 방지하기 위해서 입니다.(어떤 문자가 가진 글자 그대로의 뜻과는 다른 해석이 가능한 문자를 "특수 문자"라고 합니다. 예를 들면, 와일드 카드 문자인 *가 특수 문자입니다.)

bash$ ls -l [Vv]*
-rw-rw-r--    1 bozo  bozo       324 Apr  2 15:05 VIEWDATA.BAT
 -rw-rw-r--    1 bozo  bozo       507 May  4 14:25 vartrace.sh
 -rw-rw-r--    1 bozo  bozo       539 Apr 14 17:11 viewdata.sh

bash$ ls -l '[Vv]*'
ls: [Vv]*: No such file or directory

참고: 몇몇 프로그램이나 유틸리티들은 쿼우트된 문자열에 들어 있는 특수 문자를 재해석하거나 확장 시킬 수 있습니다. 이것은 쿼우팅의 중요한 사용법으로써 쉘이 명령어줄 매개변수를 해석하지 않고 프로그램이 해석해서 확장하도록 해 줍니다.

bash$ grep '[Ff]irst' *.txt
file1.txt:This is the first line of file1.txt.
 file2.txt:This is the First line of file2.txt.

grep [Ff]irst *.txt 라고 하면 당연히 동작하지 않습니다.

변수를 참조할 때는 보통 큰 따옴표(" ")로 묶어 주는게 좋습니다. 이렇게 하면 $, `(backquote), \(이스케이프)를 제외한 모든 특수 문자들을 보존해 줍니다. 변수에 쿼우트를 걸어서("$variable") $을 특수 문자로 인식하게 되면 그 변수의 값으로 바꿔 줍니다 (위의 예 5-1 참고).

낱말 조각남(word splitting) [1] 을 피하려면 큰 따옴표를 쓰기 바랍니다. 이렇게 하면 인자에 공백문자가 들어 있어도 하나의 낱말로 인식하게 해 줍니다.
variable1="4개의 낱말로 이루어진 변수"
COMMAND 이것은 $variable1 입니다.    # COMMAND는 6개의 인자를 가지고 실행됩니다.
# "이것은" "4개의" "낱말로" "이루어진" "변수" "입니다."

COMMAND "이것은 $variable1 입니다."  # COMMAND는 1개의 인자를 가지고 실행됩니다.
# "이것은 4개의 낱말로 이루어진 변수 입니다."


variable2=""    # 비어 있습니다.
COMMAND $variable2 $variable2 $variable2        # COMMAND는 인자 없이 실행됩니다.
COMMAND "$variable2" "$variable2" "$variable2"  # COMMAND는 3개의 빈 인자를 가지고 실행됩니다.
COMMAND "$variable2 $variable2 $variable2"      # COMMAND는 2개의 빈칸을 가진 하나의 인자를 가지고 실행됩니다.

# Thanks, S.C.

작은 정보: echo문의 인자를 큰 따옴표로 묶어주는 것은 낱말 조각남이 문제가 될 때에만 필요합니다.

예 6-1. 이상한 변수를 에코하기

#!/bin/bash
# weirdvars.sh: 이상한 변수 에코하기.

var="'(]\\{}\$\""
echo $var        # '(]\{}$"
echo "$var"      # '(]\{}$"     차이가 없죠?

echo

IFS='\'
echo $var        # '(] {}$"     \ 가 빈 칸으로 바뀌었네요.
echo "$var"      # '(]\{}$"

# S.C. 제공

exit 0

작은 따옴표(' ')도 큰 따옴표와 비슷하게 동작하지만 $의 특별한 의미를 꺼 버려서 변수 참조가 일어나지 않게 합니다. 작은 따옴표안의 '을 제외한 모든 특수 문자들은 단순히 문자 그대로 해석됩니다. 작은 따옴표("완전한 쿼우팅")를 큰 따옴표("부분 인용")보다 좀 더 엄격한 방법이라고 생각하면 됩니다.

참고: 작은 따옴표안에서는 이스케이프 문자(\)도 글자 그대로 인식되기 때문에 작은 따옴표로 묶인 문자열에 \을 써서 작은 따옴표 자체를 넣으려고 한다면 원하는 결과가 나오지 않습니다.
echo "Why can't I write 's between single quotes"

echo
# 간접적인 방법
echo 'Why can'\''t I write '"'"'s between single quotes'
#    |-------|  |----------|   |-----------------------|
# 이스케이프와 큰 따옴표로 쿼우트된 작은 따옴표에 의해 3개의 문자열로 나뉘어져 있습니다.

# Stephane Chazelas 제공.

이스케이프(Escaping)는 하나의 문자를 쿼우팅하는 방법입니다. 어떤 문자 앞에 이스케이프 문자(\)가 오면 쉘에게 그 문자를 문자 그대로 해석하게 해 줍니다.

경고

echosed같은 몇몇 명령어들에서는 이스케이핑이 특수 문자의 특수한 의미를 키도록 해 주는 반대의 효과를 가질 수도 있습니다.

특별한 의미를 갖는 몇 개의 이스케이프 문자들

echosed와 쓰임

\n

뉴라인(newline)

\r

리턴(return)

\t

탭(tab)

\v

수직탭(vertical tab)

\b

백스페이스(backspace)

\a

"경고"(alert, 비프음이나 깜빡거림)

\0xx

0xx 같은 8진수 아스키 표현으로 변환

예 6-2. 이스케이프된 문자들

#!/bin/bash
# escaped.sh: 이스케이프된 문자들

echo; echo

echo "\v\v\v\v"      # \v\v\v\v 라고 출력
# 'echo'가 이스케이프된 문자들을 출력하게 하려면 -e 옵션을 써야 됩니다.
echo -e "\v\v\v\v"   # 4 개의 수직탭 출력
echo -e "\042"       # " 출력(따옴표, 8진수 아스키 문자 42).


# Bash 버전 2 이후에서는 $'\xxx' 도 허용됩니다.
echo $'\n'
echo $'\a'
echo $'\t \042 \t'   # 탭으로 둘려쌓인 따옴표(").

# 변수에 아스키 문자를 할당하기.
# ------------------------------
quote=$'\042'        # " 를 변수로 할당.
echo "$quote 여기는 쿼우트된 부분이고, $quote 여기는 안 된 부분입니다."

echo

# 변수에 아스키 문자를 여러개 쓰기.
triple_underline=$'\137\137\137'  # 137 은 "_"의 8진수 아스키 코드.
echo "$triple_underline 밑줄 $triple_underline"

ABC=$'\101\102\103\010'           # 101, 102, 103 은 각각 8진수 A, B, C.
echo $ABC

echo; echo

escape=$'\033'                    # 033 은 이스케이프의 8진수.
echo "\"escape\" echoes as $escape"

echo; echo

exit 0

$' ' 문자열 확장의 다른 예제를 보고 싶으면 예 35-1 참고.

\"

큰 따옴표를 그냥 큰 따옴표로 해석

echo "Hello"                  # Hello
echo "\"Hello\", he said."    # "Hello", he said.

\$

달러 표시를 그냥 달러 표시로 해석(\$ 뒤에 오는 변수는 참조되지 않습니다)

echo "\$variable01"  # $variable01 이라고 찍힘

\\

백슬래쉬를 그냥 백슬래쉬로 해석

echo "\\"  # \ 라고 찍힘

참고: \ 의 동작은 자신이 이스케이프 됐는지, 쿼우트 됐는지, here document에서 쓰이는지에 따라 달라집니다.
echo \z               #  z
echo \\z              # \z
echo '\z'             # \z
echo '\\z'            # \\z
echo "\z"             # \z
echo "\\z"            # \z
echo `echo \z`        #  z
echo `echo \\z`       #  z
echo `echo \\\z`      # \z
echo `echo \\\\z`     # \z
echo `echo \\\\\\z`   # \z
echo `echo \\\\\\\z`  # \\z
echo `echo "\z"`      # \z
echo `echo "\\z"`     # \z

cat <<EOF              
\z                      
EOF                   # \z

cat <<EOF              
\\z                     
EOF                   # \z

# Stephane Chazelas 제공.

명령어의 인자 리스트 중간에 나오는 빈칸을 이스케이프 시키면 낱말 조각남을 막을 수 있습니다.
file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
# 명령어에 넘길 인자(들)의 파일 리스트.

# 리스트에 두 개를 더 추가하고 리스트를 보여줌.
ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list

echo "-------------------------------------------------------------------------"

# 빈칸을 이스케이프 시키면 어떤 일이 일어날까요?
ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
# 에러: 파일들을 구분 지어줄 빈칸을 이스케이프 시켰기 때문에
#       제일 앞 세개의 파일이 'ls -l' 에게 하나의 인자로 넘어갑니다.

이스케이프는 한 명령어를 여러줄에 걸쳐 쓸 수 있게도 해 줍니다. 보통은, 줄이 다르면 다른 명령어를 나타내지만 줄 끝에 이스케이프를 걸면 뉴라인 문자를 이스케이프시키기 때문에 그 다음줄이 원래 줄과 한 줄로 이어지게 됩니다.

(cd /source/directory && tar cf - . ) | \
(cd /dest/directory && tar xpvf -)
# 다시 보는 알란 콕스의 디렉토리 트리 복사 명령어
# 여기서는 더 읽기 쉽도록 두 줄로 나누었습니다.

# 이렇게도 가능하죠:
tar cf - -C /source/directory |
tar xpvf - -C /dest/directory
# 밑의 참고를 보세요.
# (Thanks, Stephane Chazelas.)

참고: 줄이 파이프 문자인 |으로 끝난다면 굳이 이스케이프 문자(\)를 적어줄 필요가 없습니다. 하지만, 여러줄에 걸친 하나의 명령어에서 줄 끝에 항상 이스케이프 문자를 적어주는 것은 아주 좋은 프로그래밍 습관입니다.

echo "foo
bar" 
#foo
#bar

echo

echo 'foo
bar'    # 아직은 다른점이 없죠.
#foo
#bar

echo

echo foo\
bar     # 뉴라인이 이스케이프 됐습니다.
#foobar

echo

echo "foo\
bar"     # 약한 쿼우트 안에서는 \ 가 이스케이프 문자로 해석되기 때문에 똑같습니다.
#foobar

echo

echo 'foo\
bar'     # 강한 쿼우팅 안에서 \ 는 아무 의미 없이 그냥 \ 입니다.
#foor\
#bar

# Stephane Chazelas 제공.

주석

[1]

여기서 "낱말 조각남"은 문자열이 조각나서 여러개의 인자로 인식되는 것을 뜻합니다.