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