Shell Scripting : function Error

안녕하세요.

Unix and Linux System Administration Handbook 4th Edition 공부 중에 이해가 안 되는 Error가 떠서 질문합니다.

Shell script 내용은 이렇습니다.

#!/bin/bash

function show_usage {
echo "Usage: $0 source_dir dest_dir"
exit 1
}

if [ $# -ne 2 ]; then
show_usage
else
if [ -d $1 ]; then
source_dir=$1
else
echo ‘Invalid source directory’
show_usage
fi
if [ -d $2 ]; then
dest_dir=$2
else
echo ‘Invalid destination directory’
show_usage
fi
fi

printf "Source directory is ${source_dir}\n"
printf "Destination directory is ${dest_dir}\n"

terminal에서의 명령문은 다음과 같습니다.

mkdir aaa bbb

sh showusage aaa bbb

showusage: 3: showusage: function: not found
Usage: showusage source_dir dest_dir

띄어쓰기까지 다 똑같이 썼을 것입니다.
분명 에러가 뜰 리가 없는데 이해가 되질 않습니다.
가르쳐 주신다면 감사하겠습니다.

우분투 최근 버전을 쓰고 계신 것이죠? 그렇다는 전제하에 말씀드립니다.

결론부터 이야기 하자면, 사용하신 shell이 bash가 아니라 dash이기 때문에 발생한 현상입니다. dash는 소위 POSIX 표준에 부합하는 (POSIX compliant) shell이라서 function을 사용하는 방법이 다릅니다.

저도 최근 bash 보안사고가 일어났을 때, 제가 올린 글에 workman729님이 달아주신 댓글을 보고 알게된 것이… 우분투 default shell (아무것도 건드린 것이 없을 때의 shell 들)의 구조는 조금 이상하더군요. 우분투의 default shell은 dash입니다. 그러나, 사용자의 default shell은 bash입니다. 이게 무슨 말이냐면…

(1) 우분투의 default shell : 일단 "ls -l /bin/sh"을 해보세요. 아마도 /bin/dash에 symbolic link되어 있을 것입니다. 즉, "sh"을 사용하면 "/bin/dash"가 동작합니다.

(2) 우분투의 사용자 default shell : terminal을 열고 "printenv SHELL" 명령어를 주어 보세요. "/bin/bash"가 나올 겁니다. 즉, 터미널에서 사용되는 shell은 bash라는 뜻이죠. 제가 이상하다고 위에서 말씀드린 것은 이 2개가 일치하지 않아서죠. 우분투에서 이렇게 사용하는 이유는 다음 글에 설명되어 있네요.
[url:3ihwtj2a]http://lwn.net/Articles/343924/[/url:3ihwtj2a]

진상은 이렇습니다. 위에 설명한 대로, "sh"을 사용하면 "/bin/dash"를 사용하게 됩니다. 따라서, "sh showusage aaa bbb"하고 실행시키면 "sh" 즉 "/bin/dash"가 showusage file을 읽어서 실행시킵니다. 이런 방식으로 사용하면, script 첫 줄의 "#!/bin/bash"는 comment일 뿐, 아무런 의미가 없습니다. 이런 경우, bash의 function 사용법을 사용한 script를 dash에서 사용했으니 문제가 된 것이죠. 즉, 사용하신 방법은 shell script를 실행시키는 방법 중에 가장 좋지 않은 방법이라고 말할 수 있을 듯합니다. 한마디로 사용할 shell을 스스로 선택하는 것이 아니고 시스템에 맡기는 방법이니까요.

고치는 방법

(1) bash를 사용한다.
아래와 같이 사용합니다. bash를 지정했으니, bash가 showusage file을 읽어서 실행합니다.

[code:3ihwtj2a]bash showusage aaa bbb[/code:3ihwtj2a]
혹은 아래와 같이 사용합니다. 이 경우, default shell ("sh", 즉, "/bin/dash")가 -c 다음에 주어진 argument를 command로서 실행합니다. 아래 (2)와 같은 방법이 되는 셈입니다. 이때, script의 첫 줄에 "#!/bin/bash"라고 쓰셨으니, bash가 동작하여 bash가 showusage를 실행시킵니다.

[code:3ihwtj2a]sh -c "./showusage aaa bbb"[/code:3ihwtj2a]

(2) showuasge에 실행 permission을 주어 동작시킵니다. showusage 첫 줄의 "#!/bin/bash"에 따라 bash를 동작시킵니다.

[code:3ihwtj2a]chmod u+x showusage
./showusage aaa bbb[/code:3ihwtj2a]

(3) dash shell function 구조로 script의 function을 바꿉니다. 근데, 이것은 원하는 것이 아니시죠? script의 첫 줄에 보니 "#!/bin/bash"라고 쓰여 있고, 이것은 bash script를 원한다는 뜻이잖아요? 아무튼 원하신다면 다음과 같이 바꿉니다. 즉, keyword "function"을 없애고, function 이름 다름에 "()"를 붙입니다. 위에 있는 것이 bash function이고, 아래에 있는 것이 dash function 입니다. dash script를 원하신다면, script 첫줄의 "#!/bin/bash"도 "#!/bin/dash"로 바꾸는 것이 좋겠죠.

[code:3ihwtj2a]
function sayIt {
echo "Hello, World"
}
[/code:3ihwtj2a]

[code:3ihwtj2a]
sayIt () {
echo "Hello, World"
}
[/code:3ihwtj2a]

순간 제 아이디가 나와서 놀랐습니다.

미력한 댓글이지만 어느 정도 도움이 된 것 같아 오늘 하루가 즐거울 것 같습니다.
happyman님의 말씀 처럼 하시면 도움이 될 것 같습니다.