11장. 내부 명령어(Internal Commands and Builtins)

내부 명령(builtin) 은 Bash 툴 셋에 포함된 명령어로 말 그대로 bulit in(고유의, 붙박이의)된 명령어입니다. 내부 명령은 시스템 명령어와 이름이 같을 수도 있지만 이런 경우는 Bash가 내부적으로 다시 구현해 놓은 것입니다. [1] 예를 들어, 하는 일이 거의 동일한 bash의 echo/bin/echo와 다릅니다.

키워드(keyword)는 예약된 낱말, 토큰, 연산자를 말합니다. 키워드는 쉘에서 특별한 의미를 가지면서, 쉘 문법을 형성해 줍니다. 예를 들면, "for", "while", "!"가 키워드입니다. 내부 명령(builtin)과 비슷하게 키워드도 Bash 내부에 하드코드(hard-coded)되어 있습니다.

I/O

echo

변수나 표현식을 표준출력으로 출력(예 5-1 참고).
echo Hello
echo $a

echo 에서 이스케이프 문자들을 찍으려면 -e 옵션을 주어야 합니다. 예 6-2를 참고하세요.

보통, 각각의 echo는 뉴라인을 출력해 주지만 -n 옵션을 주면 이 뉴라인을 안 찍어 줍니다.

참고: echo는 파이프에서 여러 명령어중의 하나로 쓰일 수 있습니다.

if echo "$VAR" | grep -q txt   # if [[ $VAR = *txt* ]]
then
  echo "$VAR 에는 \"txt\"" 문자열이 들어 있습니다."
fi

참고: echo명령어 치환과 같이 써서 변수 값을 세트할 수 있습니다.

a=`echo "HELLO" | tr A-Z a-z`

예 12-15, 예 12-2, 예 12-28, 예 12-29를 참고하세요.

경고

echo `command`command 출력의 모든 라인피드를 지워버리는 것에 주의하기 바랍니다. 보통 \n$IFS공백문자중 하나이기 때문에, Bash는 command의 출력에 나타나는 라인피드를 빈 칸으로 바꾸어 echo의 인자로 만들어 버립니다.

bash$  printf '\n\n1\n2\n3\n\n\n\n'

 1
 2
 3


bash $




bash$ echo "`printf '\n\n1\n2\n3\n\n\n\n'`"

 1
 2
 3
bash $
	      

참고: 이 명령어는 쉘 내장 명령으로서 /bin/echo와 비슷한 동작을 하지만 엄연히 다릅니다.

bash$ type -a echo
echo is a shell builtin
echo is /bin/echo
	      

printf

printf는 형식화된 출력을 해주는 명령어로서, echo의 확장판입니다. C 언어의 printf보다 기능이 제한된 변종으로, 문법은 C와 약간 다릅니다.

printf format-string... parameter...

/bin/printf/usr/bin/printf의 bash 내장 명령 버전입니다. 더 자세한 내용은 printf(시스템 명령어)의 맨 페이지를 참고하세요.

경고

bash의 옛날 버전은 printf를 지원하지 않을 수도 있습니다.

예 11-1. printf가 실제로 쓰이는 예제

#!/bin/bash
# printf demo

PI=3.14159265358979
DecimalConstant=31373
Message1="안녕들 하신가,"
Message2="지구인 여러분."

echo

printf "Pi를 소수점 이하 2 자리만 표시 = %1.2f" $PI
echo
printf "Pi를 소수점 이하 9 자리만 표시 = %1.9f" $PI  # 반올림이 잘 됐죠?

printf "\n"                                  # 라인피드를 찍음.
                                             # 'echo'와 똑같음.

printf "상수 = \t%d\n" $DecimalConstant  # 탭이 들어갔죠(\t)

printf "%s %s \n" $Message1 $Message2

echo

# ==========================================#
# C 함수인 'sprintf'를 시뮬레이션.
# 형식화된 문자열로 변수를 로딩.

echo 

Pi12=$(printf "%1.12f" $PI)
echo "Pi를 소수점 12 자리만 표시 = $Pi12"

Msg=`printf "%s %s \n" $Message1 $Message2`
echo $Msg; echo $Msg

# 이렇게 하면, 'sprintf' 함수는 Bash에서 로딩할 수 있는 모듈로
# 접근이 가능해 지지만, 이식성이 있는 구현은 아닙니다.

exit 0

printf는 에러 메세지를 형식화 해서 출력해 줄 때 아주 유용하게 쓰입니다.
E_BADDIR=65

var=nonexistent_directory

error()
{
  printf "$@" >&2
  # 인자로 넘어온 위치 매개변수를 형식화해서 표준에러로 출력
  echo
  exit $E_BADDIR
}

cd $var || error $"%s 로 바꿀 수 없습니다." "$var"

# Thanks, S.C.

read

변수값을 표준입력에서 "읽어 들입니다." 즉, 키보드에서 사용자의 입력을 받아 처리합니다. -a 옵션을 주면 read는 변수를 배열로 받아 들입니다(예 26-2 참고).

예 11-2. read로 변수 할당하기

#!/bin/bash

echo -n "'var1' 변수값을 넣으세요: "
# -n 옵션은 뉴라인을 제거해 줍니다.

read var1
# var1 변수가 read에 의해 세트되기 때문에 var1 앞에 '$'가 없습니다. 주의하세요.

echo "var1 = $var1"


echo

# 하나의 'read' 문으로 여러개의 변수를 세트할 수 있습니다.
echo -n "'var2'와 'var3'의 값을 넣으세요(빈 칸이나 탭으로 구분): "
read var2 var3
echo "var2 = $var2      var3 = $var3"
# 한 개 값만 주게 되면, 다른 변수(들)은 세트가 안 된 상태(널)로 남게 됩니다.

exit 0

read로 입력을 넣을 때 보통은 뉴라인 전에 \를 넣어 뉴라인을 무시하게 합니다. -r 옵션을 주면 \ 가 문자 그대로 해석되도록 합니다.

예 11-3. read로 여러줄의 입력 넣기

#!/bin/bash

echo

echo "\\ 로 끝나는 문자열을 입력하고 <ENTER> 를 누르세요."
echo "그 다음에 두 번째 문자열을 입력하고 <ENTER> 를 다시 누르세요."
read var1     # "var1"을 읽을 때 "\" 때문에 뉴라인이 제거됩니다.
              #     첫번째 줄 \
              #     두번째 줄

echo "var1 = $var1"
#     var1 = 첫번째 줄 두번째 줄

# "\"로 끝나는 줄을 만날 때 마다,
# var1에 문자를 계속 입력하기 위해서 다음 줄을 위한 프롬프트를 받게 됩니다.

echo; echo

echo "\\ 로 끝나는 다른 문자열을 입력하고 <ENTER> 를 누르세요."
read -r var2  # -r 옵션은 "\"를 문자 그대로 읽어 들입니다.
              #     첫번째 줄 \

echo "var2 = $var2"
#     var2 = 첫번째 줄 \

# 입력 데이타는 첫번째 <ENTER> 에서 끝나게 됩니다.

echo 

exit 0

read 명령어는 프롬프트를 보여준 다음 ENTER없이 키누름을 받아 들이는 재밌는 옵션을 갖고 있습니다.

# ENTER 없이 키누름을 읽음.

read -s -n1 -p "키를 누르세요 " keypress
echo; echo "당신이 누른 키는 "\"$keypress\""입니다."

# -s 는 입력을 에코하지 말라는 옵션입니다.
# -n N 은 딱 N 개의 문자만 받아 들이라는 옵션입니다.
# -p 는 입력을 읽기 전에 다음에 나오는 프롬프트를 에코하라는 옵션입니다.

# 이 옵션들은 순서가 바로 되어 있어야 하기 때문에 쓰기가 약간 까다롭습니다.

read 명령어는 표준입력으로 재지향된 파일에서 변수값을 "읽을" 수도 있습니다. 입력 파일이 한 줄 이상이라면 첫 번째 줄만 변수로 할당됩니다. read가 하나 이상의 매개변수를 갖고 있다면 각 변수는 공백 문자로 구분되는 연속적인 문자열로 할당됩니다. 조심하세요!

예 11-4. read파일 재지향과 같이 쓰기

#!/bin/bash

read var1 <data-file
echo "var1 = $var1"
# "data-file"의 첫번째 줄 전체가 var1으로 세팅

read var2 var3 <data-file
echo "var2 = $var2   var3 = $var3"
# "read"의 직관적이지 않은 행동에 주의하세요.
# 1) 입력 파일의 제일 처음으로 돌아가서,
# 2) 각 변수는 줄 전체가 아닌 공백문자로 나누어진 문자열로 세트됨.
# 3) 마지막 변수는 그 줄의 나머지로 세트.
# 4) 변수 갯수가 공백문자로 나누어진 문자열보다 많다면 나머지 변수들은 세트되지 않음.

echo "------------------------------------------------"

# 위의 문제를 루프로 해결해 보겠습니다.
while read line
do
  echo "$line"
done <data-file
# Heiner Steven 이 이 부분을 지적해 주었습니다.

echo "------------------------------------------------"

# "read"가 읽어 들이는 줄이 공백문자가 아닌 다른 문자로 구분되도록 하려면
# $IFS(내부 필드 구분자, Internal Field Separator)를 쓰면 됩니다.

echo "모든 사용자 목록:"
OIFS=$IFS; IFS=:       # /etc/passwd 는 필드 구분자로 ":"를 씁니다.
while read name passwd uid gid fullname ignore
do
  echo "$name ($fullname)"
done </etc/passwd   # I/O 재지향.
IFS=$OIFS              # 원래 $IFS 를 복구시킴.
# 이 코드도 Heiner Steven 이 제공해 주었습니다.

exit 0

파일시스템

cd

익숙한 명령어인 cd는 스크립트에서 어떤 명령어가 특정한 디렉토리에서 실행될 필요가 있을 때 그 디렉토리로 옮겨가기 위해 쓰입니다.
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
[앞서 인용했던 알란 콕스의 예제]

cd가 심볼릭 링크를 무시하고 원래 디렉토리로 가도록 하기 위해서는 -P(물리적인, physical) 옵션을 주면 됩니다.

cd - 라고 하면 바로 전 작업 디렉토리인 $OLDPWD 로 옮겨 갑니다.

pwd

현재 작업 디렉토리를 출력(Print Working Directory). 이 명령어는 사용자(혹은 스크립트)의 현재 디렉토리(예 11-5 참고)를 알려줍니다. 내부 변수인 $PWD의 값을 읽는 것과 동일한 효과를 가져옵니다.

pushd, popd, dirs

이 명령어들은 작업 디렉토리를 즐겨찾기에 기억시켜주는 메카니즘으로, 디렉토리간에 순서대로 왔다 갔다 할 수 있게 해 줍니다. 디렉토리 이름을 기억하기 위해서 푸쉬다운 스택을 사용합니다. 옵션을 줘서 디렉토리 스택에 대해서 다양한 조작을 할 수 있습니다.

pushd dir-namedir-name을 디렉토리 스택에 넣고 동시에 현재 디렉토리를 그 디렉토리로 옮겨 줍니다.

popd는 디렉토리 스택의 제일 위에 있는 디렉토리를 지우고(pop) 동시에 현재 디렉토리를 그 디렉토리로 옮겨 줍니다.

dirs은 디렉토리 스택의 목록을 보여줍니다 ($DIRSTACK과 같음). pushdpopd가 성공한다면 dirs가 자동으로 불립니다.

디렉토리 이름을 하드 코딩하지 않고 디렉토리를 여기 저기로 옮겨 다녀야 하는 스크립트가 이 명령어를 쓰면 아주 좋습니다. 스크립트 내에서 디렉토리 스택의 내용을 담고 있는 $DIRSTACK 배열 변수에 묵시적으로 접근이 가능하기 때문에 주의해야 합니다.

예 11-5. 현재 작업 디렉토리 변경하기

#!/bin/bash

dir1=/usr/local
dir2=/var/spool

pushd $dir1
# 자동으로 'dirs'를 실행합니다(디렉토리 스택의 내용을 표준출력으로 뿌림).
echo "Now in directory `pwd`." # 역따옴표(backquote)를 건 'pwd'.

# 'dir1'에서 아무 일이나 하고,
pushd $dir2
echo "지금은 `pwd` 디렉토리에 있습니다."

# 'dir2'에서 아무 일이나 한 다음,
echo "DIRSTACK 배열의 top 항목은 $DIRSTACK 입니다."
popd
echo "이제 `pwd` 디렉토리로 돌아왔습니다."

# 'dir1'에서 아무 일이나 하세요.
popd
echo "이제 원래의 `pwd` 디렉토리로 돌아왔습니다."

exit 0

변수

let

let 명령어는 변수에 대해서 산술 연산을 수행합니다. 많은 경우에 있어서 expr보다 좀 간단한 기능을 수행합니다.

예 11-6. let으로 몇 가지 산술 연산을 하기.

#!/bin/bash

echo

let a=11          # 'a=11' 과 똑같습니다.
let a=a+5         # let "a = a + 5" 와 똑같습니다.
                  # (큰따옴표와 빈 칸을 쓰면 좀 더 읽기가 편하죠)
echo "11 + 5 = $a"

let "a <<= 3"     # let "a = a << 3" 과 똑같습니다.
echo "\"\$a\" (=16) 를 3 번 왼쪽 쉬프트 = $a"

let "a /= 4"      # let "a = a / 4" 와 똑같습니다.
echo "128 / 4 = $a"

let "a -= 5"      # let "a = a - 5" 와 똑같습니다.
echo "32 - 5 = $a"

let "a = a * 10"  # let "a = a * 10" 과 똑같습니다.
echo "27 * 10 = $a"

let "a %= 8"      # let "a = a % 8" 과 똑같습니다.
echo "270 modulo 8 = $a  (270 / 8 = 33, 나머지는 $a)"

echo

exit 0
eval

eval arg1 [arg2] ... [argN]

목록에 들어 있는 인자를 명령어로 변환(스크립트 안에서 코드를 만들어 낼 때 유용함).

예 11-7. eval의 효과 보여주기

#!/bin/bash

y=`eval ls -l`   # y=`ls -l` 과 비슷하지만,
echo $y          # 라인피드가 지워집니다.

y=`eval df`      # y=`df` 와 비슷하지만,
echo $y          # 라인피드가 지워집니다.

# 라인피드(LF)가 없어지기 때문에 출력을 파싱하기가 더 쉬울 것입니다.

exit 0

예 11-8. 강제로 로그 아웃 시키기

#!/bin/bash

y=`eval ps ax | sed -n '/ppp/p' | awk '{ print $1 }'`
# 'ppp'의 프로세스 번호를 찾은 다음,

kill -9 $y   # 죽이고,

# 위에서 한 것은 다음처럼 해도 됩니다.
#  kill -9 `ps ax | awk '/ppp/ { print $1 }'


chmod 666 /dev/ttyS3
# ppp에 SIGKILL을 날리면 직렬 포트의 퍼미션이 바뀌기 때문에
# 이전 퍼미션으로 복구시켜야 됩니다.

rm /var/lock/LCK..ttyS3   # 직렬 포트용 잠금 파일을 지웁니다.

exit 0

예 11-9. "rot13" 버전

#!/bin/bash
# 'eval'로 "rot13"을 구현.
# (옮긴이: rot13 이란 각 알파벳을 13번 로테이트(그래서 이름이 rot13) 시키는 
# 간단한 암호화입니다.)
# "rot13.sh" 예제와 비교해 보세요.

setvar_rot_13()              # "rot13" 스크램블(암호화)
{
  local varname=$1 varvalue=$2
  eval $varname='$(echo "$varvalue" | tr a-z n-za-m)'
}


setvar_rot_13 var "foobar"   # "foobar" 를 rot13 시키면,
echo $var                    # sbbone

echo $var | tr a-z n-za-m    # foobar
                             # 원래 변수로 되돌림.

# Stephane Chazelas 제공.

exit 0

경고

eval 명령어는 아주 위험한 상황을 가져올 수 있기 때문에 합당한 다른 방법이 있다면 이것을 쓰지 않는 것이 좋습니다. eval $COMMANDSrm -rf * 처럼 유쾌하지 않은 값을 갖고 있을 수도 있는 COMMANDS을 실행 시킵니다. 모르는 사람이 작성한 잘 모르는 코드에 대해서 eval를 실행 시키는 것은 아주 위험합니다.

set

set 명령어는 내부 스크립트 변수값을 바꿔줍니다. 스크립트의 행동을 결정하는 옵션 플래그를 키거나 끄는 역할에 쓰이기도 하고 특정 명령어의 결과(set `command`)를 위치 매개변수로 리셋 시켜서 스크립트가 그 명령어의 결과를 필드별로 파싱할 수 있게 해 줍니다.

예 11-10. 위치 매개변수와 set 쓰기

#!/bin/bash

# "set-test" 스크립트

# 3개의 인자를 줘서 실행시키세요.
# 예를 들면, "./set-test one two three".

echo
echo "set \`uname -a\` 하기 전의 위치 매개변수:"
echo "첫번째 명령어줄 인자 = $1"
echo "두번째 명령어줄 인자 = $2"
echo "세번째 명령어줄 인자 = $3"

echo

set `uname -a` # `uname -a` 의 출력을 위치 매개변수로 세트

echo "set \`uname -a\` 한 다음의 위치 매개변수:"
# $1, $2, $3... 이 `uname -a` 의 결과로 다시 초기화됩니다.
echo "'uname -a' 의 첫번째 필드 = $1"
echo "'uname -a' 의 두번째 필드 = $2"
echo "'uname -a' 의 세번째 필드 = $3"
echo

exit 0

예 10-2 참고.

unset

unset 명령어는 쉘 변수를 효과적으로 (null)로 세트를 해서 그 변수를 지우는 효과를 가져옵니다. 이 명령어는 위치 매개변수에 대해서 동작하지 않는 것에 주의하세요.

bash$ unset PATH

bash$ echo $PATH

bash$ 

예 11-11. 변수를 "언셋"(unset) 하기

#!/bin/bash
# unset.sh: 변수를 언셋하기.

variable=hello                       # 초기화.
echo "variable = $variable"

unset variable                       # 언셋.
                                     # variable=   라고 하는 것과 동일
echo "(unset) variable = $variable"  # $variable 는 널.

exit 0
export

export 명령어는 현재 실행중인 스크립트나 쉘의 모든 자식 프로세스가 변수를 사용할 수 있게 해 줍니다. 불행하게도 스크립트나 쉘을 부른 부모 프로세스에게 변수를 다시 export 할 방법은 없습니다. export시스템 구동(startup) 파일에서 환경 변수를 초기화하고 그 다음에 생성될 사용자 프로세스들이 그 변수에 접근할 수 있게 해주는 아주 중요한 용도로 쓰입니다.

예 11-12. export를 써서, 내장된 awk 스크립트에 변수를 전달하기

#!/bin/bash

# "column totaler" 스크립트(col-totaler.sh)의 또 다른 버전.
# 대상 파일의 주어진 지정된 컬럼을 모두 더해줌.
# 여기서는 스크립트 변수를 'awk'에게 전달하기 위해서 환경(environment)을 사용합니다.

ARGS=2
E_WRONGARGS=65

if [ $# -ne "$ARGS" ] # 원하는 수 만큼의 명령어줄 인자가 넘어왔는지 확인.
then
   echo "사용법: `basename $0` filename column-number"
   exit $E_WRONGARGS
fi

filename=$1
column_number=$2

#===== 여기까지는 원래 스크립트와 똑같습니다 =====#

export column_number
# column_number 를 환경으로 export 해서 awk 스크립트에서 다시 꺼내갈수 있게 함.


# awk 스크립트 시작.
# ------------------------------------------------
awk '{ total += $ENVIRON["column_number"]
}
END { print total }' $filename
# ------------------------------------------------
# awk script 끝.


# Stephane Chazelas 제공.

exit 0

작은 정보: export var1=xxx라고 해서 초기화와 export를 한번에 할 수도 있습니다.

declare, typeset

declaretypeset 명령어는 변수의 특성을 지정하거나 제한해 줍니다.

readonly

declare -r과 같은 역할을 하는 명령어로서, 어떤 변수를 읽기 전용으로 만들어 주는 것인데, 결국 상수로 쓰겠다는 것입니다. 이런 변수값을 바꾸려고 한다면 에러 메세지를 만나게 됩니다. C 언어의 const 형지정자와 비슷한 것으로 보면 됩니다.

getopts

이것은 아주 강력한 도구로서 명령어 줄에서 스크립트로 넘어온 인자를 파싱해 줍니다. C 프로그래머들에게 익숙한 getopt 라이브러리 함수의 bash 버전입니다. 스크립트로 넘어오는 여러개의 옵션 [2] 과 그 해당 인자들을 처리해줍니다(예를 들면, scriptname -abc -e /usr/local).

getopts는 내부적으로 두 개의 변수를 사용합니다. $OPTIND(OPTion INDex)는 인자 포인터이고 $OPTARG(OPTion ARGument)는 옵션에 딸려 넘어오는 해당 인자(선택적)입니다. 선언 태그의 옵션 이름뒤에 콜론이 있으면 해당 인자가 있다는 뜻입니다.

getopts는 보통 while 루프와 같이 써서 옵션과 인자를 한 번에 하나씩 처리하고 $OPTIND 변수값을 하나씩 줄여서 그 다음을 처리하게 합니다.

참고:

  1. 명령어 줄에서 인자앞에는 빼기(-)나 더하기(+)를 적어 줘야 하는데 이 접두사가 있어야 getopts가 명령어 줄 인자를 옵션으로 인식할 수 있습니다. 실제로는, -+가 빠져 있는 첫번째 인자를 만나면 바로 종료하게 됩니다.

  2. getops는 표준 while 루프와 약간 다른 형태로 조건 대괄호가 빠져 있는 형태입니다.

  3. 예전의 getopt 대신 getopts가 새롭게 쓰입니다.

while getopts ":abcde:fg" Option
# 초기 선언.
# a, b, c, d, e, f, g 옵션(플래그)만 지원.
# 'e' 뒤의 :로 'e' 옵션에는 인자가 있어야 된다는 것을 나타냄
do
  case $Option in
    a ) # 'a'일 경우 할 일.
    b ) # 'b'일 경우 할 일.
    ...
    e)  # 'e'일 경우 할 일, $OPTARG 로 'e' 뒤에 따라오는 해당 인자를 처리.
    ...
    g ) # 'g'일 경우 할 일.
  esac
done
shift $(($OPTIND - 1))
# 인자 포인터를 다음으로 이동.

# 보이는 것처럼 그렇게 복잡하지는 않습니다. <씨익>.
	      

예 11-13. getopts로 스크립트로 넘어온 옵션과 인자 읽기

#!/bin/bash

# 'getopts' 는 스크립트로 넘어온 명령어줄 인자를 처리해 줍니다.
# 인자들은 "옵션"(플래그)과 해당 인자로 파싱됩니다.

# 이렇게 실행시켜 보세요.
# 'scriptname -mn'
# 'scriptname -oq qOption' (qOption 은 아무런 문자열이면 됩니다.)
# 'scriptname -qXXX -r'
#
# 'scriptname -qr'    - 원하던 결과가 안 나오는데, "r"이 "q" 옵션의 추가 인자로 처리되기 때문입니다.
# 'scriptname -q -r'  - 위와 똑같이 원치 않는 결과.
# 어떤 옵션에 추가 인자("flag:")가 필요하다고 설정이 되면 
# 명령어줄에서 자기 바로 다음에 나오는 것을 무조건 자신의 인자로 받아들입니다.

NO_ARGS=0 
OPTERROR=65

if [ $# -eq "$NO_ARGS" ]  # 인자 없이 불렸군요.
then
  echo "사용법: `basename $0` options (-mnopqrs)"
  exit $OPTERROR          # 인자가 주어지지 않았다면 사용법을 알려주고 종료.
fi  
# 사용법: scriptname -options
# 주의: 대쉬(-)가 필요합니다.


while getopts ":mnopq:rs" Option
do
  case $Option in
    m     ) echo "1번 시나리오: option -m-";;
    n | o ) echo "2번 시나리오: option -$Option-";;
    p     ) echo "3번 시나리오: option -p-";;
    q     ) echo "4번 시나리오: option -q-, \"$OPTARG\"를 줘서";;
    # 'q' 옵션은 추가 인자가 있어야 하는데 없다면 디폴트로 처리됩니다.
    r | s ) echo "5번 시나리오: option -$Option-"'';;
    *     ) echo "구현되지 않은 옵션이 선택됐습니다.";;   # 디폴트
  esac
done

shift $(($OPTIND - 1))
# 인자 포인터를 감소시켜서 다음 인자를 가르키게 합니다.

exit 0

스크립트 동작

source, . ((dot) 명령어)

이 명령어가 명령어 줄에서 불린다면 해당 스크립트를 실행 시킵니다. 스크립트에서 source file-name이라고 불린다면 file-name을 읽어 들일 것입니다. C/C++의 #include 지시자와 같은 역할을 합니다. 여러개의 스크립트가 공통으로 쓰이는 데이타 파일이나 함수 라이브러리를 써야 할 경우같은 상황에서 유용합니다.

예 11-14. 데이타 파일 "포함하기"

#!/bin/bash

. data-file    # 데이타 파일 로드.
# "source data-file"이라고 하는 것과 같지만, 이게 좀 더 이식성 있는 방법입니다.

# "data-file"은 'basename'으로 참조되기 때문에 현재 디렉토리에 꼭 있어야 됩니다.

# 이제 그 파일에 들어 있는 몇개의 데이타를 참조해 보겠습니다.

echo "variable1 (data-file 에 있는 변수) = $variable1"
echo "variable3 (data-file 에 있는 변수) = $variable3"

let "sum = $variable2 + $variable4"
echo "variable2 + variable4 (data-file 에 있는 변수) = $sum"
echo "message1 (data-file 에 있는 변수) 은 \"$message1\""
# 주의:                                    이스케이프된 쿼우트

print_message 이것은 data-file 의 message-print 함수가 보여주는 메세지입니다.


exit 0

위의 예 11-14에서 data-file은 같은 디렉토리에 있어야 합니다.

# error free
# (옮긴이:
# 제일 윗줄에 한글이 들어가 있으면 file data-file 이라고 했을 때 
# data-file: International language text
# 라고 나오네요. 이것 때문인지 예제에서 이 파일을 . 하면 에러가 납니다.
# 맨 위의 error free 는 이 에러를 피하기 위한 것입니다.)
#
#
# 이것은 스크립트에 의해 읽히는 데이타 파일입니다.
# 이런 종류의 파일은 보통 변수나 함수등을 담고 있습니다.
# 쉘 스크립트에 의해 'source'나 '.'로 적재될 수 있습니다.

# 변수를 초기화 합시다.

variable1=22
variable2=474
variable3=5
variable4=97

message1="안녕, 잘 지냈어?"
message2="이젠 됐어. 안녕."

print_message ()
{
# 넘어온 어떤 메세지도 다 에코시킴.

  if [ -z "$1" ]
  then
    return 1
    # 인자가 없으면 에러.
  fi

  echo

  until [ -z "$1" ]
  do
    # 함수로 넘어온 인자를 하나씩 모두 처리.
    echo -n "$1"
    # 라인 피드를 없애면서 한 번에 하나씩 에코.
    echo -n " "
    # 낱말 사이에 빈 칸을 집어 넣음.
    shift
    # 다음.
  done  

  echo

  return 0
}  
exit

스크립트를 무조건 끝냄. exit는 정수값을 인자로 받아서 쉘에게 스크립트의 종료 상태를 알려줄 수도 있습니다. 아주 간단한 스크립트가 아니라면 스크립트의 마지막에 exit 0처럼 성공적인 실행을 알려 주는 것은 아주 좋은 습관입니다.

참고: 만약에 exit가 인자 없이 쓰인다면 그 스크립트의 종료 상태는 exit를 제외하고 가장 마지막에 실행된 명령어의 종료 상태로 됩니다.

exec

이 쉘 내부 명령은 현재의 프로세스를 주어진 명령어로 대치시킵니다. 보통은 쉘이 어떤 명령어를 만나면 그 명령어를 실행하기 위해서 자식 프로세스를 포크 [3] 시킵니다. 하지만 exec 내장 명령은 포크를 하지 않고 exec된 그 명령어로 쉘 자체를 대치시킵니다. 그렇기 때문에 스크립트에서 이 명령어가 쓰이면 exec된 명령어가 종료할 때 스크립트가 강제로 종료됩니다. 이런 이유로, exec을 스크립트에서 쓰려면 아마도 제일 마지막 명령어로 써야 할 겁니다.

exec는 또한 파일 디스크립터를 재할당 할 때도 쓰입니다. exec <zzz-file은 표준입력을 zzz-file으로 바꿔줍니다(예 16-1 참고).

예 11-15. exec 효과

#!/bin/bash

exec echo "\"$0\" 를 종료합니다."   # 스크립트에서 종료.

# 다음 줄은 절대 실행되지 않습니다.

echo "여기는 절대 에코되지 않습니다."

exit 0  # 역시, 여기서 종료되지도 않고요.

참고: find 명령어의 -exec 옵션은 exec 쉘 내장 명령과 다릅니다.

shopt

이 명령어는 쉘이 실행중에 옵션을 바꿀 수 있게 해 줍니다(예 24-1예 24-2 참고). 이는 종종 Bash 시스템 구동 파일(startup files)에서 쓰이는 데 일반 스크립트에서도 쓰일 수 있습니다. 이 명령어는 bash 버전 2나 그 다음 버전부터 쓸 수 있습니다..
shopt -s cdspell
# 'cd'를 쓸 때 디렉토리 이름이 약간 틀린 것 정도는 자동으로 고쳐줍니다.

명령어

true

단순히 성공적(0)인 종료 상태를 리턴하는 명령어.

# 무한 루프
while true   # ":" 의 별칭(alias)
do
   operation-1
   operation-2
   ...
   operation-n
   # 루프를 빠져 나갈 방법이 필요.
done

false

단순히 실패한 종료 상태를 리턴하는 명령어.

# 널 루프
while false
do
   # 이 부분은 실행되지 않음.
   operation-1
   operation-2
   ...
   operation-n
   # 아무 일도 안 일어납니다!
done   

type [cmd]

외부 명령어인 which와 비슷하게 주어진 "cmd"의 완전한 경로명을 보여 줍니다. 하지만, which와는 다르게 type는 bash 내장 명령입니다. 유용한 옵션인 -a를 주면 주어진 "cmd"키워드인지 내장 명령인지를 알려주고 똑같은 이름의 시스템 명령어가 있다면 그 위치도 알려줍니다.

bash$ type '['
[ is a shell builtin
bash$ type -a '['
[ is a shell builtin
[ is /usr/bin/[
	      

hash [cmds]

주어진 명령어의 경로명을 쉘 해쉬 테이블에 저장해서 그 명령어가 다시 불릴 때 $PATH에서 찾지 않도록 해줍니다. hash를 인자 없이 쓰면 자신이 해쉬하고 있는 목록을 보여줍니다. -r 옵션은 해쉬 테이블을 초기화 합니다.

help

help 명령어는 쉘 내장 명령에 대한 간략한 사용법을 알려줍니다. whatis와 동일하지만 내장 명령에 대해서 쓰인다는 점이 다릅니다.

bash$ help exit
exit: exit [n]
    Exit the shell with a status of N.  If N is omitted, the exit status
    is that of the last command executed.
	      

주석

[1]

이렇게 하는 이유는 성능상의 문제(보통은 fork를 해야 하는 외부 명령어보다 더 빠르게 실행)이거나 특정 내부 명령의 경우에 쉘 내부 변수로 직접 접근할 필요가 있기 때문입니다.

[2]

옵션은 플래그처럼 동작하는 인자로서 스크립트의 행동을 키거나 꺼 줍니다. 특정 옵션과 관련이 있는 인자는 그 옵션(플래그)이 나타내는 행동을 키거나 끄게 합니다.

[3]

명령어나 쉘 자신이 어떤 작업을 수행하기 위해 새로운 하위 프로세스를 초기화(혹은 spawn)하는 것을 포크(fork)라고 합니다. 이 때, 새롭게 생긴 프로세스를 "자식" 프로세스라고 하고, 그 자식 프로세스를 포크한 프로세스를 "부모" 프로세스라고 합니다. 자식 프로세스가 자신에게 주어진 일을 하는 동안 부모 프로세스도 계속 자신의 일을 해 나갑니다.