35장. Bash, 버전 2

지금 여러분이 시스템에서 쓰고 있는 Bash 는 버전 2.XX 대 입니다.
bash$ echo $BASH_VERSION
2.04.21(1)-release
	      
예전 버전에 비해 배열 변수 [1] 문자열및 매개변수 확장, 향상된 변수 간접 참조등을 포함한 다른 특징들이 업데이트 됐습니다.

예 35-1. 문자열 확장

#!/bin/bash

# 문자열 확장.
# Bash 버전 2 부터 소개됨.

# $'xxx' 형태의 문자열들은 표준 이스케이프 문자로 해석됩니다.

echo $'벨 3번 울리기 \a \a \a'
echo $'3번의 폼피드(form feed) \f \f \f'
echo $'10번의 뉴라인(newline) \n\n\n\n\n\n\n\n\n\n'

exit 0

예 35-2. 간접 변수 참조 - 새로운 방법

#!/bin/bash

# 변수 간접 참조.
# C++ 의 참조 특성이 약간 가미되었습니다.


a=letter_of_alphabet
letter_of_alphabet=z

echo "a = $a"           # 직접 참조.

echo "Now a = ${!a}"    # 간접 참조.
# ${!variable} 표기법은 예전의 "eval var1=\$$var2" 보다 훨씬 좋습니다.

echo

t=table_cell_3
table_cell_3=24
echo "t = ${!t}"        # t = 24
table_cell_3=387
echo "t 의 값이 ${!t} 로 바뀌었습니다."    # 387

#  배열이나 테이블 멤버를 참조할 때나 
#+ 다차원 배열을 시뮬레이션 할 경우에 쓸 만 합니다.
# 인덱싱을 옵션으로 가졌다면 더 좋았을 겁니다(휴~~).

exit 0

예 35-3. 배열과 약간의 트릭을 써서 한 벌의 카드를 4명에게 랜덤하게 돌리기

#!/bin/bash
# 오래된 시스템이라면 #!/bin/bash2 로 실행시켜야 할지도 모릅니다.

# 카드:
# 카드 한 벌을 4명에게 무작위로 돌리기.

UNPICKED=0
PICKED=1

DUPE_CARD=99

LOWER_LIMIT=0
UPPER_LIMIT=51
CARDS_IN_SUIT=13
CARDS=52

declare -a Deck
declare -a Suits
declare -a Cards
# 하나짜리 3차원 배열이라면 더 쉽고 더 직관적이었을 겁니다.
# 아마 Bash 다음 버전에서는 다차원 배열을 지원할 겁니다.


initialize_Deck ()
{
i=$LOWER_LIMIT
until [ "$i" -gt $UPPER_LIMIT ]
do
  Deck[i]=$UNPICKED   # 카드 "한 벌"(deck)을 모두 안 고른 상태로 둠.
  let "i += 1"
done
echo
}

initialize_Suits ()
{
Suits[0]=C # 클로버(Clubs)
Suits[1]=D # 다이아몬드(Diamonds)
Suits[2]=H # 하트(Hearts)
Suits[3]=S # 스페이드(Spades)
}

initialize_Cards ()
{
Cards=(2 3 4 5 6 7 8 9 10 J Q K A)
# 배열을 초기화하는 또 다른 방법.
}

pick_a_card ()
{
card_number=$RANDOM
let "card_number %= $CARDS"
if [ "${Deck[card_number]}" -eq $UNPICKED ]
then
  Deck[card_number]=$PICKED
  return $card_number
else  
  return $DUPE_CARD
fi
}

parse_card ()
{
number=$1
let "suit_number = number / CARDS_IN_SUIT"
suit=${Suits[suit_number]}
echo -n "$suit-"
let "card_no = number % CARDS_IN_SUIT"
Card=${Cards[card_no]}
printf %-4s $Card
# 카드를 칸 단위로 깔끔하게 출력.
}

seed_random ()  # 랜덤 넘버 발생기 seed. 
{
seed=`eval date +%s`
let "seed %= 32766"
RANDOM=$seed
}

deal_cards ()
{
echo

cards_picked=0
while [ "$cards_picked" -le $UPPER_LIMIT ]
do
  pick_a_card
  t=$?

  if [ "$t" -ne $DUPE_CARD ]
  then
    parse_card $t

    u=$cards_picked+1
    # 임시로 1 부터 인덱싱하는 형태로 바꿈.
    let "u %= $CARDS_IN_SUIT"
    if [ "$u" -eq 0 ]   # 중첩된 if/then 조건 테스트.
    then
     echo
     echo
    fi
    # 다른 사람.

    let "cards_picked += 1"
  fi  
done  

echo

return 0
}


# 구조적 프로그래밍:
# 전체 프로그램 로직은 함수로 모듈화 되어 있습니다.

#================
seed_random
initialize_Deck
initialize_Suits
initialize_Cards
deal_cards

exit 0
#================



# 연습문제 1:
# 전체에 대해 완전한 주석을 달아보세요.

# 연습문제 2:
# 한 사람이 받은 카드를 종류순으로 정렬해서 보여주도록 고쳐보세요.
# 필요하다면 편리한 다른 기능을 써도 괜찮습니다.

# 연습문제 3:
# 스크립트를 간략화하고, 합리적인 로직이 되도록 고쳐보세요.

주석

[1]

Chet Ramey 는 펄에서 쓰이는 연관 배열(associative arrays) 을 다음 Bash 에서 사용할 수 있도록 하겠다고 약속했습니다.