투란도트(Turandot): 수수께끼는 세 개, 그러나 죽음은 하나! 칼라프(Caleph): 아니오, 수수께끼는 세 개, 생명이 하나! | |
푸치니(Puccini) |
변수명에 예약어나 예약 문자를 할당하기.
case=value0 # 문제가 생깁니다. 23skidoo=value1 # 역시 문제가 생깁니다. # 숫자로 시작하는 변수명은 쉘이 예약해 놓았습니다. # 대신 _23skidoo=value1 를 쓰세요. 밑줄로 시작하는 변수는 괜찮습니다. # 하지만... 밑줄로만 된 변수명은 제대로 동작하지 않습니다. _=25 echo $_ # $_ 은 마지막 명령어의 마지막 인자로 세트되는 특수한 변수입니다. xyz((!*=value2 # 심각한 문제가 생깁니다. |
변수명에 하이픈이나 다른 예약 문자를 쓰기.
var-1=23 # 대신 'var_1' 를 쓰세요. |
변수와 함수 이름을 똑같이 쓰기. 이렇게 하면 스크립트를 이해하기가 힘듭니다.
do_something () { echo "이 함수는 \"$1\" 로 뭔가를 합니다." } do_something=do_something do_something do_something # 문법적으로는 다 맞지만 엄청나게 헷갈리죠? |
적절치 못한 곳에 공백문자를 쓰는 것(다른 프로그래밍 언어들과는 달리 bash는 공백문자에 대해 좀 까다로운 반응을 보입니다).
var1 = 23 # 'var1=23' 이 맞습니다. # 이렇게 하면 bash는 "="과 "23" 인자를 갖는 "var1" 명령어를 실행시키려고 합니다. let c = $a - $b # 'let c=$a-$b' 나 'let "c = $a - $b"' 가 맞습니다. if [ $a -le 5] # if [ $a -le 5 ] 가 맞습니다. # if [ "$a" -le 5 ] 라고 하면 더 좋겠죠. # [[ $a -le 5 ]] 도 동작합니다. |
초기화 안 된 변수(값이 할당되기 전의 변수)가 "0" 인 것으로 가정하기. 초기화 안 된 변수의 값은 0이 아니라 "널"(null)입니다.
테스트 문에서 = 과 -eq 를 섞어 쓰기.= 은 문자를 비교하는 것이고 -eq 은 정수를 비교하는 것임을 명심하세요.
if [ "$a" = 273 ] # $a 가 정수인가요? 문자열인가요? if [ "$a" -eq 273 ] # $a 가 정수라면. # 때때로 반대의 결과없이 -eq 와 = 를 섞어 쓸 수 있습니다. # 하지만... a=273.0 # 정수가 아닙니다. if [ "$a" = 273 ] then echo "비교가 잘 됩니다." else echo "비교가 잘 안 됩니다." fi # 비교가 잘 안 됩니다. # a=" 273" 와 a="0273" 도 마찬가집니다. # 비슷하게, 정수가 아닌 숫자에 "-eq"를 쓰는것도 문제를 일으킵니다. if [ "$a" -eq 273.0 ] then echo "a = $a' fi # 에러 메세지를 내고 abort 됩니다. # test.sh: [: 273.0: integer expression expected |
가끔은 "테스트" 대괄호([ ])안에서 쓰이는 변수를 큰 따옴표로 쿼우트 해줘야 할 필요가 있습니다. 이렇게 안 하면 예상치 못한 결과가 나올 수 있습니다. 예 7-5, 예 16-2, 예 9-5를 참고하세요.
스크립트의 소유자가 스크립트에서 쓰이는 명령어에 대한 실행 권한이 없어서 그 명령어를 실행시키지 못할 수도 있습니다. 명령어 줄에서 실행 시키지 못하는 명령어는 스크립트에서 실행시킨다 해도 역시 실패할 것입니다. 해결책은, 문제가 되는 명령어의 속성을 바꿔보는데, 정 안 되면 suid 비트를 설정해 보기 바랍니다.(당연히 루트로 해야겠죠).
- 를 재지향 연산자(실은 아니지만)처럼 쓰려고 하면 보통은 이상한 결과를 가져옵니다.
command1 2> - | command2 # command1의 에러 출력을 파이프 로 재지향 하려고 하지만... # ...제대로 동작하지 않을 겁니다. command1 2>& - | command2 # 역시 실패합니다. Thanks, S.C. |
Bash 버전 2 이상에만 들어 있는 기능은 에러 메세지를 내면서 실패할 경우가 있습니다. 오래된 리눅스 머신의 경우에는 기본 설치시 Bash 버전 1.XX 대가 깔려 있을 수 있습니다.
#!/bin/bash minimum_version=2 # Chet Ramey 가 Bash 에 기능을 계속 추가하고 있기 때문에 # $minimum_version 을 2.XX 나 적당한 버전으로 세트하면 됩니다. E_BAD_VERSION=80 if [ "$BASH_VERSION" \< "$minimum_version" ] then echo "이 스크립트는 Bash 버전 $minimum_version 이나 그 이상에서만 동작합니다." echo "Bash 를 업그레이드할 것을 강력히 추천합니다." exit $E_BAD_VERSION fi ... |
리눅스 머신이 아닌 곳의 본 쉘 스크립트(#!/bin/sh)에서 Bash 전용 기능을 쓰게 되면 예상치 못한 동작을 할 수 있습니다. 리눅스에서는 sh이 보통 bash를 나타내지만 일반적인 유닉스에서도 항상 이렇지는 않습니다.
도스 형태의 뉴라인(\r\n)으로 된 스크립트는 실행되지 않는데, #!/bin/bash\r\n이 우리가 원하는 #!/bin/bash\n과 같지 않아서 인식할 수 없기 때문입니다. 이럴 때는 그 스크립트가 유닉스 식의 뉴라인을 갖도록 변환해 줘야 합니다.
스크립트 헤더가 #!/bin/sh이라고 되어 있으면 완전한 Bash 호환 모드로 동작하지 않을 수 있습니다. 몇몇 Bash 전용 기능들을 못 쓸 수도 있습니다. 완전한 Bash 전용 확장 기능을 쓰고 싶은 스크립트는 헤더를 #!/bin/bash라고 해 주면 됩니다.
스크립트는 변수를 자기 부모 프로세스나 쉘, 환경으로 export 할 수 없습니다. 우리가 생물학 시간에 배웠듯이 자식은 부모에게서 물려 받지만 그 반대는 안 됩니다.
WHATEVER=/home/bozo export WHATEVER exit 0 |
bash$ echo $WHATEVER bash$ |
서브쉘 안에서 변수를 세트하고 조작한 다음 서브쉘 바깥에서 똑같은 변수를 사용하려고 한다면 별로 유쾌하지 않은 결과를 가져 옵니다.
예 32-1. 서브쉘 함정(Subshell Pitfalls)
#!/bin/bash # 서브쉘 변수의 함정. outer_variable=outer echo echo "outer_variable = $outer_variable" echo ( # 서브쉘 시작 echo "서브쉘 안에서의 outer_variable = $outer_variable" inner_variable=inner # 셋 echo "서브쉘 안에서의 inner_variable = $inner_variable" outer_variable=inner # 변수값 변경이 전역적으로 영향을 받을까요? echo "서브쉘 안에서의 outer_variable = $outer_variable" # 서브쉘 끝 ) echo echo "서브쉘 밖에서의 inner_variable = $inner_variable" # 언셋. echo "서브쉘 밖에서의 outer_variable = $outer_variable" # 안 변했죠. echo exit 0 |
스크립트 안에서 "suid" 명령어를 쓰면 시스템 보안을 위협할 수 있습니다. [1]
쉘 스크립트를 CGI 프로그래밍에 쓰는 것은 문제가 있습니다. 쉘 스크립트 변수는 "타입 세이프"(typesafe)하지 않기 때문에 CGI에 대해서만은 원하는 결과를 가져 오지 않을 수 있습니다. 게다가 "크래커 대해서 안전한"(cracker-proof) 쉘 스크립트를 짜는 것은 굉장히 어렵습니다.
Danger is near thee -- Beware, beware, beware, beware. Many brave hearts are asleep in the deep. So beware -- Beware. | |
A.J. Lamb and H.W. Petrie |
[1] | 스크립트에 suid 소유권을 거는 것은 무의미 합니다. |