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 에서는 일어나지 않는지 알 수 있습니다.
Brace expansion, Process substitution 은
sh
에서는 사용할 수 없습니다.
명령 실행 방법
그동안 프롬프트에서 직접 명령을 타입 하여 실행하였다면 활용하여 다음과 같이 실행할 수 있습니다.
$ export CROSS=arm-linux-gnueabi
$ export CC=${CROSS}-gcc
$ export OBJDUMP=${CROSS}-objdump
$ $CC -c 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 는 확장과 치환 이전에 처리됩니다.
그러므로 명령치환 이나 변수확장의 결과로 키워드나 alias 또는
대입연산이 나온다면 이미 해석 작업이 끝난 상태이므로 제대로 기능하지 않습니다.
# 키워드
$ 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 # 되었으나 이미 해석이 끝난 상태이므로 외부 명령으로 인식.
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
# Good
$ curl -O 'http://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html'
# find 명령에 인수를 전달할 때도 마찬가지 (tmp.* 는 shell globbing 과 중복된다.)
$ find /tmp -name 'tmp.*'