Shell Metacharacters
Shell script 에서는 프로그래밍 언어에서처럼 여러 연산자를 제공하지 않습니다. 가령 산술연산을 위해서는 별도로 제공되는 특수 표현식을 사용하거나 외부 명령을 사용합니다. 하지만 명령문을 작성할때 사용되는 메타문자들이 있습니다. 이 메타문자들은 명령문 상에서 특별한 기능을 하므로 동일한 문자가 명령문의 스트링에 포함될 경우 반드시 escape 하거나 quote 해야 오류가 발생하지 않습니다.
C++ 소스코드를 대신 생성해주는 템플릿 프로그래밍을 메타프로그래밍 이라고 하죠, 또한 Makefile 을 해당 플랫폼에 맞게 대신 생성해주는 cmake, qmake 같은 프로그램을 메타 make 이라고 합니다. shell 에서 메타문자는 명령문이 해석되어 실행을 위한 구문을 정의하게 될 때 사용됩니다.
( ) ` | & ;
&& || # AND, OR 문자
< > >> # redirection 문자
* ? [ ] # glob 문자
" ' # quote 문자
\ $
= += # 대입연산
( )
- subshell 을 생성할 때
- $( ) 명령 치환에 사용
- 명령 grouping 에 사용
- 우선순위 조절에도 사용
프로그래밍 언어에서처럼 공백이나 ;
에 대한 제약 없이 자유롭게 사용할 수 있는 메타문자입니다.
`
backtick 문자는 $( )
와 함께 명령 치환에 사용됩니다.
&& ||
AND, OR 논리 메타문자.
이것은 shell 메타문자로 [[ ]]
특수 표현식에서 제공하는 연산자와는 다른 것입니다.
shell 에서 &&
||
메타문자는 우선순위를 같게 취급합니다.
&
명령을 background 로 실행할때 사용합니다.
또한 ;
와 동일하게 명령문의 종료를 나타내므로 명령을 한줄에 연이어 쓸경우 ;
를 붙여서는 안됩니다.
command1 &; command2 & # error
command1 & command2 & # OK
{ ... command & ;} # error
{ ... command & } # OK
|
명령들을 파이프로 연결할때 사용합니다.|&
는 stdout, stderr 둘 다 파이프로 전달하며 2>&1 |
와 동일한 의미입니다.
< > >>
이외에도 redirection 에 관련된 여러 메타문자들
>>
(append) , >&
, <>
, &>
( >word 2>&1 ) , &>>
( >>word 2>&1 ) , >|
(override noclobber), <<
(here document) , <<-
(no leading tab) , <<<
(here string)
;
한줄에 여러 명령을 연이어 사용할때 분리를 위해 사용합니다.;;
, ;&
, ;;&
는 case 문에서 각 pattern)
의 종료를 나타내는데 사용됩니다.
* ? [ ]
패턴매칭에 사용되는 glob 문자도 메타문자에 속합니다.
" '
문장을 quote 할때 사용합니다.
space 로 분리된 문장을 quote 하면 하나의 인수가 됩니다.
\
escape 할때 사용되는 문자입니다.
$
매개변수 확장, 산술 확장, 명령 치환 에 사용됩니다.
= +=
대입연산에 사용됩니다. +=
는 sh 에서는 사용할 수 없습니다.
메타문자가 명령문에 포함되면 escape 해야한다.
&
메타문자가 들어간 파일 이름을 인수로 사용해 오류 발생
# background process 가 실행되면서 오류발생
$ find * -name foo&bar.txt
[1] 16962
bar.txt: command not found
[1]+ Done
# 다음과 같이 수정합니다.
$ find * -name foo\&bar.txt
$ find * -name foo"&"bar.txt
$ find * -name foo'&'bar.txt
$ find * -name "foo&bar.txt"
$ find * -name 'foo&bar.txt'
find 명령은 인수로 shell 메타문자인 ( )
, ;
을 사용하는데 quote 하거나 escape 하지 않으면
정상적으로 실행이 되지 않습니다.
# '( )' 는 shell 메타문자 이므로 명령문에 바로 사용할시 오류발생
$ find * -type f ( -name "*.log" -or -name "*.bak" ) -exec rm -f {} ;
bash: syntax error near unexpected token '('
# '( )' 는 quote 했으나 ';' 메타문자로 인해 오류발생
$ find * -type f '(' -name "*.log" -or -name "*.bak" ')' -exec rm -f {} ;
find: missing argument to '-exec'
# 다음과 같이 shell 메타문자 들을 모두 escape 합니다.
$ find * -type f '(' -name "*.log" -or -name "*.bak" ')' -exec rm -f {} ';'
no error !
$ find * -type f \( -name "*.log" -or -name "*.bak" \) -exec rm -f {} \;
no error !
메타문자는 shell 에서 특별히 취급하는 문자다.
그러므로 다른 단어와 공백 없이 붙여서 사용할 수도 있다.
# 메타문자 일경우 '( )'
$ (true)&&(true;false); echo $?
$ 1
# 메타문자가 아닐경우 '{ }'
$ {true;}&&{true;false;}; echo $?
bash: syntax error near unexpected token `}'
{ } 는 shell keyword
{ }
는 메타문자가 아니고 키워드로 명령 그룹에 사용되며,
이외에도 함수 정의, 매개변수 확장, brace 확장에 사용됩니다.
# 명령 그룹
{ echo 1; echo 2; echo 3 ;} # 명령 위치에서 사용되므로 shell keyword
# 함수 정의
f1() { echo 1 ;}
# 매개변수 확장
$AA, ${AA}, ${AA:-0}, ${AA//Linux/Unix}
# brace 확장
echo file{1..5}
---------------------------
# 다음과 같은 경우는 find 명령의 인수에 해당하는 문자
$ find * -name '*.o' -exec rm -f {} \;
' ! ' shell keyword
!
도 키워드로 쉘에서 두 가지 기능을 가지고 있습니다.
하나는 명령의 위치에서 공백과 함께 사용되면 logical NOT 으로 사용되고,
다른 하나는 프롬프트 상에서 !
와 공백 없이 붙여서 command history 확장에 사용됩니다.
- logical NOT
$ true; echo $?
0
$ ! true; echo $? # 명령 위치에서 사용되면 logical NOT 키워드
1
$ if true; then echo 111; else echo 222; fi
111
$ if ! true; then echo 111; else echo 222; fi
222
---------------------------------------------
# 다음과 같은 경우는 명령 위치가 아니므로 logical NOT 키워드로 사용된 것이 아니고
# [, test, find 명령의 인수에 해당하는 문자입니다.
$ [ ! 1 -eq 1 ] ...
$ if test ! 2 -eq 3; then ...
$ find * ! -name "*.o" ...
command history 확장
!
는 프롬프트 상에서 history 확장에 사용되므로 주의해야 합니다.
(non-interactive shell 인 script 파일 실행시에는 history 기능이 disable 됩니다).
$ lsb_release -d
Description: Ubuntu 15.04
$ echo command history : !lsb # 이전 명령 history 확장
command history : lsb_release -d
$ echo "hello!lsb world" # double quotes 에서도 history 확장이 된다!
hellolsb_release -d world
대입 메타문자로 += 를 사용할 수 있다.
+=
대입 메타문자는 변수 와 array 에서 모두 사용할 수 있습니다.
( sh 에서는 사용할 수 없습니다. )
##### variable #####
$ AA=hello
$ AA+=" world"
$ echo "$AA"
hello world
##### indexed array #####
$ ARR=(11 22 33)
$ ARR+=(44)
$ echo ${ARR[@]}
11 22 33 44
##### associative array #####
$ ARR=([i1]=aaa [i2]=bbb [i3]=ccc)
$ ARR+=([i4]=ddd)
$ echo ${ARR[@]}
ccc bbb aaa ddd
$ echo ${!ARR[@]}
i3 i2 i1 i4
Shell 에는 ' , ' 분리자가 없다
기본적으로 명령의 인수나 array 원소를 구분하기 위해 공백을 사용합니다.
f1() {
echo arg1 : $1
echo arg2 : $2
echo arg3 : $3
}
$ f1 11 , 22 , 33
arg1 : 11
arg2 : , # ',' 가 두번째 인수가 됩니다.
arg3 : 22
-------------------
$ ARR=(11,22,33)
$ echo ${AA[0]}
11,22,33
$ echo ${AA[1]}
$
$ echo ${AA[2]}
$
그러므로 명령에서 ,
를 이용하여 옵션인수를 받는경우 공백을 두어서는 안됩니다.
# ',' 를 공백없이 붙여야 한다.
$ strace -e trace=open,close,read,write command ...
OK
# 다음과 같이 공백을 두면 안됩니다.
$ strace -e trace=open, close, read, write command ...
ERROR
$ gcc -shared -Wl,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
OK