Expansions and Substitutions

프롬프트에서 명령문을 작성한후 enter 키를 누르면 shell 은 먼저 명령문을 tokens (words) 으로 분리한 다음 해석해야할 표현식이 있을경우 변수확장, 산술확장, 명령치환 을 거쳐 최종 변경된 명령문을 만들게 됩니다. 그리고나서 마지막으로 불필요한 quotes 을 삭제 처리합니다. 확장과 치환이 이루어 질때 shell 은 다음과 같은 순서로 진행을 합니다.

1 . Brace expansion
2 . Tilde expansion

3 . 다음 4 개는 left-to-right 순서로 동시에 처리 됩니다.

  • Parameter expansion
  • Arithmetic expansion
  • Command substitution
  • Process substitution
$ i=1
$ echo $i $((i++)) $i   # left-to-right 순서로 처리되므로
1 1 2                   # 결과로 '1 1 1' 이 아니라 '1 1 2' 가 나왔습니다.

4 . Word splitting
5 . Pathname expansion (globbing)

위 순서를 보면 변수확장, 명령치환 결과에 대해서는 왜 단어분리와 globbing 이 일어나는지 알 수 있고, globbing 결과에 대해서는 단어분리가 일어나지 않는지 알 수 있습니다.

Brace expansion, Process substitution 은 sh 에서는 사용할 수 없습니다.

명령 실행 방법

그동안 프롬프트에서 직접 명령을 타입 하여 실행하였다면 활용하여 다음과 같이 실행할 수 있습니다.

$ export CROSS=arm-linux-gnueabi
$ export CC=${CROSS}-gcc
$ export OBJDUMP=${CROSS}-objdump

$ $CC hello.c -o hello
$ $OBJDUMP -d hello

------------------------------------

$ AA="echo hello world"

$ $AA       # 단어분리에 의해 echo 는 명령이 되고 hello world 는 인수가 된다.
hello world

$ "$AA"     # quote 을 하면 "echo hello world" 가 명령 이름이 된다.
echo hello world: command not found

확장과 치환 이전에 해석되는 것들

키워드, 메타문자, 대입연산, quotes, \ 문자를 이용한 escape, alias, ! 문자를 이용한 명령 history 는 확장과 치환 이전에 해석됩니다. 그러므로 변수확장 이나 명령치환의 결과로 키워드나 메타문자 또는 대입연산이 나온다면 이미 해석이 끝난 상태이므로 제대로 기능하지 않습니다.

키워드, 메타문자, quotes ... 해석    >>    확장과 치환    >>    최종 결과물을 이용해 명령문 실행

# 키워드
$ var=[[
$ $var a != b ]]; echo  $?  # $var 변수가 확장된 후는 이미 키워드 해석이 끝난 상태이므로
[[: command not found       # '[[' 를 키워드로 인식하지 못한다.

# 메타문자
$ var="echo hello | wc -c"
$ $var                      # $var 변수에서 단어분리가 일어나 echo hello | wc -c 명령문이
hello | wc -c               # 되었으나 '|' 를 파이프 메타문자로 인식하지 못한다.

# redirection
$ A='>'
$ echo 12345 $A file        # $A 변수 확장 결과 echo 12345 > file 명령이 되었으나
12345 > file                # 실제 redirection 처리가 되지 않는다.
$ cat file
cat: file: No such file or directory

# 대입연산
$ var="AA=100"              
$ $var                      # $var 변수의 확장 결과 AA=100 대입연산이 되었으나
AA=100: command not found   # 이미 해석이 끝난 상태이므로 AA=100 외부 명령으로 인식한다.

# quotes
$ var='"aaa bbb"'
$ args.sh $var              # $var 변수가 확장되어 args.sh "aaa bbb" 명령문이 되었으나
$1 : "aaa                   # 기존처럼 quotes 이 해석되어 하나의 인수가 되지 않는다.
$2 : bbb"                   

$ var='echo "hello"'         
$ $var                      # $var 변수 확장 결과 echo "hello" 명령문이 되었으나
"hello"                     # quotes 삭제 처리가 되지 않고 남아있다.

# '\' 문자를 이용한 escape
$ expr 2 \* 3               # '*' 는 glob 문자이므로 escape 해야 오류가 발생하지 않습니다.
6
$ var="2 \* 3"
$ expr $var                 # $var 변수가 확장되어 위와 같은 expr 2 \* 3 명령문이 되었으나
expr: syntax error          # '\*' escape 문자 처리가 되지 않아 오류가 발생합니다.

# '!' 문자를 이용한 history 확장
$ var='!!'
$ $var                      # $var 변수가 확장되어 이전 명령을 나타내는 !! history 명령이
!!: command not found       # 되었으나 이미 해석이 끝난 상태이므로 외부 명령으로 인식한다.

예를 들어 다음과 같이 변수값에 포함된 quotes 은 sed 명령에서 사용하기 위해 escape 할 필요가 없습니다. ( 왜냐하면 quotes 해석은 변수 확장전에 처리 되므로 )

$ AA='"foo"'

# 변수값에 quotes 이 포함됨
$ echo $AA
"foo"

# 변수값에 포함된 quotes 은 escape 할 필요가 없다.
$ echo 'var = "foo"' | sed "s/$AA/(&)/"
var = ("foo")

만약에 확장과 치환의 결과로 나온 키워드, 메타문자, quotes, 대입연산 ... 이 처리되기를 원하면 eval 명령을 사용하면 됩니다.

shell 에서 제공하는 기능과 특정 명령이 사용하는 형식이 중복되는 경우

다음은 curl 명령에서 사용하는 인수 형식인데요. {a,b,c} 는 shell 에서 사용하는 brace 확장 기능과 중복됩니다. 그러므로 인수가 정상적으로 curl 명령에 전달되기 위해서는 quote 을 해야합니다.

$ curl -O http://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html

$ curl -O http://example.com/archive[1996-1999]/vol[1-4]/part'{a,b,c}'.html   # OK

$ curl -O 'http://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html'   # OK

find 명령에 인수를 전달할 때도 마찬가지 ( tmp.* 는 shell globbing 과 중복된다. )

$ find /tmp -name 'tmp.*'

; 문자는 shell 에서 명령문의 끝을 나타내는 문자로 사용되므로 다음과 같이 escape 해야 정상적으로 명령이 실행됩니다. { } 문자는 명령문의 시작 위치에서만 shell 키워드로 인식되므로 아래의 경우는 escape 할 필요가 없습니다.

$ nft add chain ip filter FORWARD { type filter hook forward priority 0; policy drop; }

$ ... FORWARD { type filter hook forward priority 0\; policy drop\; }    # OK

$ ... FORWARD { type filter hook forward priority 0';' policy drop';' }  # OK