Глава 34. Bash, версия 2

Текущая версия Bash, та, которая скорее всего установлена в вашей системе, фактически -- 2.XX.Y.

bash$ echo $BASH_VERSION
2.05.b.0(1)-release
        
В этой версии классического языка сценариев Bash были добавлены переменные-массивы, [1] расширение строк и подстановка параметров, улучшен метод косвенных ссылок на переменные.

Пример 34-1. Расширение строк

#!/bin/bash

# "Расширение" строк (String expansion).
# Введено в Bash, начиная с версии 2.

# Строки вида  $'xxx'
# могут содержать дополнительные экранированные символы.

echo $'Звонок звенит 3 раза \a \a \a'
echo $'Три перевода формата \f \f \f'
echo $'10 новых строк \n\n\n\n\n\n\n\n\n\n'

exit 0

Пример 34-2. Косвенные ссылки на переменные -- новый метод

#!/bin/bash

# Косвенные ссылки на переменные.


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

Пример 34-3. Простая база данных, с применением косвенных ссылок

#!/bin/bash
# resistor-inventory.sh
# Простая база данных, с применением косвенных ссылок.

# ============================================================== #
# Данные

B1723_value=470                                   # сопротивление (Ом)
B1723_powerdissip=.25                             # рассеиваемая мощность (Вт)
B1723_colorcode="желтый-фиолетовый-коричневый"    # цветовая маркировка
B1723_loc=173                                     # где
B1723_inventory=78                                # количество (шт)

B1724_value=1000
B1724_powerdissip=.25
B1724_colorcode="коричневый-черный-красный"
B1724_loc=24N
B1724_inventory=243

B1725_value=10000
B1725_powerdissip=.25
B1725_colorcode="коричневый-черный-оранжевый"
B1725_loc=24N
B1725_inventory=89

# ============================================================== #


echo

PS3='Введите ноиер: '

echo

select catalog_number in "B1723" "B1724" "B1725"
do
  Inv=${catalog_number}_inventory
  Val=${catalog_number}_value
  Pdissip=${catalog_number}_powerdissip
  Loc=${catalog_number}_loc
  Ccode=${catalog_number}_colorcode

  echo
  echo "Номер по каталогу $catalog_number:"
  echo "Имеется в наличии ${!Inv} шт. [${!Val} Ом / ${!Pdissip} Вт]."
  echo "Находятся в лотке # ${!Loc}."
  echo "Цветовая маркировка: \"${!Ccode}\"."

  break
done

echo; echo

# Упражнение:
# ----------
# Переделайте этот сценарий так, чтобы он использовал массивы вместо косвенных ссылок.
# Какой из вариантов более простой и интуитивный?


# Примечание:
# ----------
#  Язык командной оболочки не очень удобен для написания приложений,
#+ работающих с базами данных.
#  Для этой цели лучше использовать языки программирования, имеющие
#+ развитые средства для работы со структурами данных,
#+ такие как C++ или Java (может быть Perl).

exit 0

Пример 34-4. Массивы и другие хитрости для раздачи колоды карт в четыре руки

#!/bin/bash
# На старых системах может потребоваться вставить #!/bin/bash2.

# Карты:
# раздача в четыре руки.

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]=Т # Трефы
Suits[1]=Б # Бубны
Suits[2]=Ч # Червы
Suits[3]=П # Пики
}

initialize_Cards ()
{
Cards=(2 3 4 5 6 7 8 9 10 В Д K Т)
# Альтернативный способ инициализации массива.
}

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=`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:
# Упростите логику сценария.

Notes

[1]

Chet Ramey обещал ввести в Bash ассоциативные массивы (они хорошо знакомы программистам, работающим с языком Perl) в одном из следующих релизов Bash.