Quotes
Shell 에서 두 번째로 중요한 개념은 quotes 이라고 할 수 있습니다. shell 에서 quotes 은 숫자나 스트링 값을 구분하기 위한 용도로 사용하지 않습니다. 123, "123", '123' 은 모두 같고 abc, "abc", 'abc' 들은 차이가 없으며 모두 다 shell 에서는 스트링입니다. shell 에서 quotes 은 다음과 같은 용도로 사용됩니다.
공백으로 분리되는 여러 개의 인수 들를 하나의 인수로 만들 때
( sed, awk 스크립트를 quotes 을 이용해 작성하는 이유가 하나의 인수로 만들기 위해서입니다. )라인개행이나 둘 이상의 공백문자를 유지하기위해
shell 키워드, 메타문자, alias 와 같이 shell 에서 특수기능을 하는 문자, 단어를 단순히 명령문의 스트링으로 만들기위해
문자 그대로 스트링을 강조하기 위해
최종적으로 명령이 실행될 때는 사용된 quotes 이 제거된 후에 인수가 전달됩니다.
-------- args.sh --------
#!/bin/bash
echo arg1 : "$1"
echo arg2 : "$2"
-------------------------
$ ./args.sh 111 "111" # quote 을 한것과 하지않은 것은 차이가 없다
arg1 : 111
arg2 : 111
$ ./args.sh hello world # 두개의 인수를 나타낸다.
arg1 : hello
arg2 : world
$ ./args.sh "hello world" # quote 을 하면 한개의 인수가됨
arg1 : hello world
arg2 :
------------------------
$ AA="hello world"
$ ./args.sh $AA # 두개의 인수를 나타낸다.
arg1 : hello
arg2 : world
$ ./args.sh "$AA" # quote 을 하면 한개의 인수가됨
arg1 : hello world
arg2 :
------------------------
# sed 스크립트를 quote 하지 않아 오류 발생
$ sed -n 1p; 2p; 3p file
ERR
# quote 을 해야 sed 스크립트가 하나의 인수로 전달됩니다.
$ sed -n '1p; 2p; 3p' file
OK
------------------------
#!/bin/bash
# 다음 세 명령은 모두 같고 차이가 없습니다.
# 명령문에서 사용된 quotes 은 실행될 때 shell 에 의해 자동으로 제거됩니다.
ls -al
ls "-al"
"ls" '-al'
특수 기능을 갖는 문자들
다음 세 문자는 shell 메타문자로 명령행 상에서 특수한 기능을 가집니다.
문자 | 기능 |
---|---|
$ | 매개변수 확장, 산술 확장, 명령 치환에 사용 |
` | 명령 치환에 사용 (backtick) |
! | history 확장에 사용 (프롬프트상 에서만) |
$ AA=hello
$ echo $AA world `date +%Y` # $AA 변수가 확장이 되고 date 명령치환이 되었다.
hello world 2015
# 특수문자를 escape 할 경우
$ echo \$AA world \`date +%Y\`
$AA world `date +%Y`
특수기능을 갖는 문자나 단어를 escape 하는 방법
shell 에서는 escape 할때 \
문자 외에 quote 을 사용할 수 있습니다. quote 을 하면 특수기능이 없어지고 단순히 명령문을 위한 스트링으로 사용됩니다.
# ( ) ; shell 메타문자를 quote 하여 기능을 상실. find 명령을 위한 스트링이 되었다.
# \( \) \; 한것과 같습니다.
$ find * -type f '(' -name "*.log" -or -name "*.bak" ')' -exec rm -f {} ';'
# background job 을 생성할때 사용되는 & 메타문자를 quote 하여 기능을 상실.
# \& 한것과 같습니다.
$ echo hello '&'
hello &
# 메타문자인 \ 을 quote 하여 기능을 상실. 결과적으로 \n 가 되었다.
# \\n 한것과 같습니다.
$ echo hello world | tr ' ' '\'n
hello
world
# grep 명령에 설정돼 있는 alias 가 escape 되었다.
# \grep 한것과 같습니다.
$ 'grep' 2015-07 data.txt
...
# shell 키워드인 time 이 escape 되어 외부명령인 /usr/bin/time 이 실행되었다.
# \time 한것과 같습니다.
$ 'time'
Usage: time [-apvV] [-f format] [-o file] [--append] [--verbose]
[--portability] [--format=format] [--output=file] [--version]
[--quiet] [--help] command [arg...]
No quotes
위에서 살펴본 바와 같이 no quotes 상태에서는 shell 키워드, 메타문자, alias, glob 문자, quotes, space, tab, newline 모두를 escape 하여 해당 기능을 disable 할 수 있습니다.
명령행 상에서 공백은 인수를 구분하는데 사용됩니다. 둘 이상의 공백은 의미가 없으므로 하나의 공백으로 대체됩니다. no quotes 에서는 공백도 escape 할 수 있습니다. 공백을 escape 하면 두 개의 인수가 하나가 됩니다.
----------- test.sh -----------
#!/bin/bash
echo arg1 : "$1"
echo arg2 : "$2"
-------------------------------
$ ./test.sh hello world # 인수가 2개
arg1 : hello
arg2 : world
$ ./test.sh hello\ world # 공백을 escape 하여 인수가 하나가 되었다.
arg1 : hello world
arg2 :
$ echo hello world # 둘 이상의 공백은 하나로 줄어든다.
hello world
$ echo hello\ \ \ \ \ \ \world # 공백이 유지된다.
hello world
no quotes 상태에서 escape 문자 사용예.
# 모든 문자가 escape 되므로 t n 이 echo 명령에 전달된다.
$ echo -e foo\tbar\n123
footbarn123
# 다음과 같이 하면 \t \n 이 echo 명령에 전달되어 escape 문자가 처리된다.
$ echo -e foo\\tbar\\n123
foo bar
123
!
history 확장 escape
$ date
Sat Jul 18 00:06:41 KST 2015
$ echo hello !!
echo hello date # 이전 명령 history 확장
hello date
$ echo hello \!! # escape 하여 history 확장 기능 disable
hello !!
\
문자 escape
# no quotes 상태에서는 모든문자가 escape 되므로 'tr : n' 와 같아진다.
$ echo 111:222:333 | tr : \n
111n222n333
$ echo 111:222:333 | tr : \\n # tr : '\n' 와 같은 결과
111
222
333
"
, '
quote 문자 escape
$ echo double quotes \" , single quotes \'
double quotes " , single quotes '
행의 마지막에 \
를 붙이고 개행을 하면 \newline
과 같이 되어
newline 을 escape 한 결과를 같습니다. (\
뒤에 다른 문자가 오면 안됨)
$ echo "I like \
> winter and \
> snow"
I like winter and snow # newline 이 escape 되어 기능을 상실해 한줄이됨.
Double quotes ( " " )
Double quotes 안에서는 $
`
!
특수기능을 하는 문자들이 해석되어 실행되고 공백과 개행이 유지됩니다. 변수 사용 시에도 동일하게 적용되므로 quote 을 하지 않으면 공백과 개행이 유지되지 않습니다.
$ echo "I
> like
> winter and snow"
I # 공백과 개행이 유지 된다.
like
winter and snow
##### 변수 사용시 #####
$ AA="this is
two lines"
$ echo $AA # 공백과 개행이 유지되지 않는다.
this is two lines
$ echo "$AA" # 공백과 개행이 유지된다.
this is
two lines
Double quotes 에서 escape 되는 문자들
double quotes 과 single quotes 의 차이점이 이것입니다.
double quotes 에서는 위의 문자들이 특수한 기능을 가지고 사용되기 때문에 \
문자로 escape 할 수가 있는 반면
single quotes 에서는 문자 그대로 출력됩니다.
$ echo '\$' # single quotes
\$
$ echo "\$" # double quotes 에서는 특수 기능을 하는 '$' 문자가 escape 된다.
$
$ echo '\`'
\`
$ echo "\`"
`
$ echo '\\'
\\
$ echo "\\"
\
$ echo 'quotes\
test'
quotes\ # single quotes 은 문자 그대로 표시된다.
test
$ echo "quotes\
test"
quotestest # double quotes 에서는 newline 이 escape 되어 한줄로 나온다.
double quotes 을 사용할때 한가지 주의해야될 사항은 !
문자를 이용한 command history 확장
이 double quotes 에서도 일어난다는 것입니다.
이것은 command history 기능이 사용되는 프롬프트 상에서만 적용되는 사항으로 스크립트 파일을 작성할 때는
해당되지 않습니다.
자세한 내용은 해당 페이지를 참조하세요.
Array 와 관련된 특수기능
double quotes 은 array 와 관련해서 특수한 기능이 있는데 전체 원소를 나타내는 ${arr[@]}
를 quote 하면 그 의미는 "${arr[0]}" "${arr[1]}" "${arr[2]}" ...
와 같게 되고 ${arr[*]}
를 quote 하게 되면 그 의미는 "${arr[0]}X${arr[1]}X${arr[2]}X..."
와 같게 됩니다. 여기서 X
는 IFS 값의 첫번째 문자를 나타냅니다."$@"
, "$*"
positional parameters 에서도 동일하게 적용됩니다.
변수값이 null 일때 quote 한것과 안한것의 차이
$ AA=""
# quote 을 하지 않으면 인수에 포함되지 않는다.
$ args.sh 1 2 $AA 3
$0 : /home/mug896/bin/args.sh
$1 : 1
$2 : 2
$3 : 3
$ args.sh 1 2 "$AA" 3
$0 : /home/mug896/bin/args.sh
$1 : 1
$2 : 2
$3 : # null 값 인수
$4 : 3
가령 다음과 같은 스크립트에서 $comp
변수값이 -c
일 경우는
tmpfile 이 컴파일된 오브젝트 파일이 되고
$comp
변수값이 null 일 경우는 링크가 완료된 실행파일을 만들고자 한다면
$comp
변수를 quote 하면 안되겠죠.
왜냐하면 quote 에의해 null 값이 하나의 인수로 전달되어
두 번째와 같이 오류가 발생하기 때문입니다.
gcc $comp -o tmpfile hello.c
........................................
gcc "" -o tmpfile hello.c
gcc: error: : No such file or directory
변수를 quote 하는 것은 오류 메시지 출력에도 영향을 줍니다. 변수를 quote 하면 좀 더 명확한 오류메시지가 출력됩니다. 따라서 명령문을 작성할 때 위와 같은 경우가 아니라면 변수를 quote 해서 사용하는 것이 좋습니다.
$ AA=""
$ echo hello 2>& $AA # echo hello 2>&
bash: $AA: ambiguous redirect
# quote 을 하면 좀 더 명확한 오류메시지가 출력된다.
$ echo hello 2>& "$AA" # echo hello 2>& ""
bash: "$AA": Bad file descriptor
....................................
$ echo hello > $AA # echo hello >
bash: $AA: ambiguous redirect
$ echo hello > "$AA" # echo hello > ""
bash: : No such file or directory
Single quotes ( ' ' )
별다른 기능 없이 모든 문자를 있는 그대로 표시합니다. escape 도 되지 않습니다. 이 안에서 single quotes 을 사용하려면 뒤에 이어지는 $' '
을 사용해야 합니다.
$ AA=hello
$ echo '$AA world
> `date`
> \$AA
> '
$AA world
`date`
\$AA
single quotes 사용시 '
문자 입력 방법
$ echo 'foo'\''bar'
foo'bar
$ echo 'foo'"'"'bar'
foo'bar
Single quotes 사용이 필요한 경우
command string 이나 trap handler 를 작성할 때 double quotes 을 사용하면 작성 당시에 변수값이 확장되어 정의가 되므로 실행 시에 원하는 값이 표시되지 않을 수 있습니다.
1. command string 에서
$ AA=100;
$ sh -c "AA=200; echo $AA" # double quotes 사용
100
$ sh -c 'AA=200; echo $AA' # single quotes 사용
200
다음은 find 명령을 이용해 ~/.cache 내에 있는 각 디렉토리 별로 디스크 사용량을 조회하는 것인데요.
-exec 옵션에는 실행할 외부 명령을 작성하고 \;
로 끝을 표시합니다.
이때 명령 스트링에서 사용된 {}
가 매칭 된 디렉토리 명으로 치환되어 실행됩니다.
$ find ~/.cache -maxdepth 1 -type d -exec du -hs {} \; | sort -hr
1.9G /home/mug896/.cache
601M /home/mug896/.cache/google-chrome
471M /home/mug896/.cache/mozilla
110M /home/mug896/.cache/apt-file
...
...
위와 동일한 역할을 하는 명령을 single, double quotes 을 비교해보기 위해
sh -c
를 이용해 작성한 것입니다.
명령문에
sh -c
를 사용하는 것은 command line 개념을 참고하세요.
# double quotes 을 사용할 경우
# {} 가 find 명령에 전달되어 디렉토리 명으로 바뀌기 전에 $( du ... ) 가 처리되므로 오류 발생
$ find ~/.cache -maxdepth 1 -exec \
sh -c "if test -d '{}'; then echo \"$( du -hs '{}' )\"; fi" \; | sort -hr
du: cannot access '{}': No such file or directory
# single quotes 을 사용할 경우는 정상적으로 실행됨
$ find ~/.cache -maxdepth 1 -exec \
sh -c 'if test -d "{}"; then echo "$( du -hs "{}" )"; fi' \; | sort -hr
OK
# 다음은 xargs 명령을 이용한 것인데 -exec 의 경우와 동일하게 적용됩니다.
$ find ~/.cache -maxdepth 1 -print0 |
xargs -0i sh -c 'if test -d "{}"; then echo "$( du -hs "{}" )"; fi' | sort -hr
OK
2. trap handler 에서
해당 페이지 참조
3. prompt 설정에서
해당 페이지 참조
$' '
이건 ' '
와 같은데 escape 문자를 사용할 수 있습니다.
escape 문자가 처리되고 난 결과는 $
가 제외된 ' '
상태가 됩니다.
sh
에서는 사용할 수 없습니다.
$ echo $'I like\n\'winter\'\tand\t\'snow\'' # \n, \t, \' escape 문자가 사용되었다.
I like
'winter' and 'snow'
------------------------------------------
$ IFS=$'\n'
$ IFS=$' \t\n'
Quotes 을 서로 붙여 사용하기
두개의 quotes 을 공백을 두지 않고 서로 붙이면 하나가 됩니다. 이 원리는 변수를 포함하는 명령 스트링을 만들거나 함수에 전달할 인수를 하나로 만들때 유용하게 사용할 수 있습니다.
명령 스트링을 만들 때
' '
을 사용해 명령문을 작성하였는데 그 안에 shell 변수를 사용할 일이 생기면 다음과 같이' '
을 분리한 후 double quotes 한 변수를 공백 없이 붙여 사용하면 됩니다. 변수를 quote 하지 않거나 single quotes 과 double quotes 사이에 공백이 있으면 안됩니다.
# 원본 명령
sed -r 's/foo/bar/g'
# single quotes 을 분리한 후 shell 변수를 double quotes 하여 공백 없이 붙인다.
sed -r 's/'"$var1/$var2"'/g'
awk 는 스크립트 내에서 $
문자를 사용하기 때문에 기본적으로 single quotes 을 이용해 작성하는데요.
다음의 경우를 보면 -exec 옵션에 사용된 sh -c
명령이 single quotes 에의해 작성되고 있는데
그 안에 있는 awk 명령에서도 single quotes 이 사용되고 있습니다.
이와 같은 경우 다음과 같이 일단 quotes 을 분리한후 \'
나 "'"
를 사용해 연결하면
sh -c '...'
내에서 실행되는 awk 명령에서도 single quotes 을 사용할 수 있습니다.
$ find * -name 'Packages*' -type f -exec \
sh -c 'echo $(md5sum -b "{}" | awk '\''{print $1}'\'') $(stat -c %s "{}") "{}"' \;
b49dd0f63bca9b3a139c5af3dd94c816 380 Packages
e805c26ff46c6e138e3cd198cff281ea 301 Packages.bz2
997a7252f202566a1e5fdc5b50c2ffdf 283 Packages.gz
명령의 인수를 만들 때
명령에 인수를 만들어 전달할 때도 두 quotes 을 서로 붙여 사용하면 하나의 인수가 됩니다.
$ ./args.sh 11 "hello "$'$world \u2665' 33 # $' ' quotes 을 사용
$0 : ./args.sh
$1 : 11
$2 : hello $world ♥
$3 : 33