Мендель Купер - Искусство программирования на языке сценариев командной оболочки
if [ "$1" -lt "$lower_limit" -o "$1" -gt "$upper_limit" ]
then
return $FALSE # Выход за границы массива.
fi
row=$2
let "left = $row * $ROWS" # Левая граница.
let "right = $left + $COLS - 1" # Правая граница.
if [ "$1" -lt "$left" -o "$1" -gt "$right" ]
then
return $FALSE # Выхол за нижнюю строку.
fi
return $TRUE # Координаты корректны.
}
IsAlive () # Проверка наличия "живой" особи в ячейке.
# Принимает массив и номер ячейки в качестве входных аргументов.
{
GetCount "$1" $2 # Подсчитать кол-во "живых" соседей.
local nhbd=$?
if [ "$nhbd" -eq "$BIRTH" ] # "Живая".
then
return $ALIVE
fi
if [ "$3" = "." -a "$nhbd" -eq "$SURVIVE" ]
then # "Живая" если перед этим была "живая".
return $ALIVE
fi
return $DEAD # По-умолчанию.
}
GetCount () # Подсчет "живых" соседей.
# Необходимо 2 аргумента:
# $1) переменная-массив
# $2) cell номер ячейки
{
local cell_number=$2
local array
local top
local center
local bottom
local r
local row
local i
local t_top
local t_cen
local t_bot
local count=0
local ROW_NHBD=3
array=( `echo "$1"` )
let "top = $cell_number - $COLS - 1" # Номера соседних ячеек.
let "center = $cell_number - 1"
let "bottom = $cell_number + $COLS - 1"
let "r = $cell_number / $ROWS"
for ((i=0; i<$ROW_NHBD; i++)) # Просмотр слева-направо.
do
let "t_top = $top + $i"
let "t_cen = $center + $i"
let "t_bot = $bottom + $i"
let "row = $r" # Пройти по соседям в средней строке.
IsValid $t_cen $row # Координаты корректны?
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_cen]} = "$ALIVE1" ] # "Живая"?
then # Да!
let "count += 1" # Нарастить счетчик.
fi
fi
let "row = $r - 1" # По верхней строке.
IsValid $t_top $row
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_top]} = "$ALIVE1" ]
then
let "count += 1"
fi
fi
let "row = $r + 1" # По нижней строке.
IsValid $t_bot $row
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_bot]} = "$ALIVE1" ]
then
let "count += 1"
fi
fi
done
if [ ${array[$cell_number]} = "$ALIVE1" ]
then
let "count -= 1" # Убедиться, что сама проверяемая ячейка
fi #+ не была подсчитана.
return $count
}
next_gen () # Обновить массив, в котором содержится информация о новом "поколении".
{
local array
local i=0
array=( `echo "$1"` ) # Преобразовать в массив.
while [ "$i" -lt "$cells" ]
do
IsAlive "$1" $i ${array[$i]} # "Живая"?
if [ $? -eq "$ALIVE" ]
then # Если "живая", то
array[$i]=. #+ записать точку.
else
array[$i]="_" # Иначе -- символ подчеркивания
fi #+ (который позднее заменится на пробел).
let "i += 1"
done
# let "generation += 1" # Увеличить счетчик поколений.
# Подготовка переменных, для передачи в функцию "display".
avar=`echo ${array[@]}` # Преобразовать массив в строку.
display "$avar" # Вывести его.
echo; echo
echo "Поколение $generation -- живых особей $alive"
if [ "$alive" -eq 0 ]
then
echo
echo "Преждеверменное завершение: не осталось ни одной живой особи!"
exit $NONE_ALIVE # Нет смысла продолжать
fi #+ если не осталось ни одной живой особи
}
# =========================================================
# main ()
# Загрузить начальное поколение из файла.
initial=( `cat "$startfile" | sed -e '/#/d' | tr -d 'n' |
sed -e 's/./. /g' -e 's/_/_ /g'` )
# Удалить строки, начинающиеся с символа '#' -- комментарии.
# Удалить строки перевода строки и вставить пробелы между элементами.
clear # Очистка экрана.
echo # Заголовок
echo "======================="
echo " $GENERATIONS поколений"
echo " в"
echo " игре " ЖИЗНЬ""
echo "======================="
# -------- Вывести первое поколение. --------
Gen0=`echo ${initial[@]}`
display "$Gen0" # Тлько вывод.
echo; echo
echo "Поколение $generation -- живых особей $alive"
# -------------------------------------------
let "generation += 1" # Нарастить счетчик поколений.
echo
# ------- Вывести второе поколение. -------
Cur=`echo ${initial[@]}`
next_gen "$Cur" # Обновить и вывести.
# ------------------------------------------
let "generation += 1" # Нарастить счетчик поколений.
# ------ Основной цикл игры ------
while [ "$generation" -le "$GENERATIONS" ]
do
Cur="$avar"
next_gen "$Cur"
let "generation += 1"
done
# ==============================================================
echo
exit 0
# --------------------------------------------------------------
# Этот сценарий имеет недоработку.
# Граничные ячейки сверху, снизу и сбоков остаются пустыми.
# Упражнение: Доработайте сценарий таким образом, чтобы ,
# + левая и правая стороны как бы "соприкасались",
# + так же и верхняя и нижняя стороны.
Пример A-12. Файл с первым поколением для игры "Жизнь"
# Это файл-пример, содержащий "поколение 0", для сценария "life.sh".
# --------------------------------------------------------------
# Игровое поле имеет размер 10 x 10, точкой обозначается "живая" особь,
#+ символом подчеркивания -- пустая ячейка. Мы не можем использовать пробелы,
#+ для обозначения пустых ячеек, из-за особенностей строения массивов в Bash.
# [Упражнение для читателей: объясните, почему?.]
#
# Строки, начинающиеся с символа '#' считаются комментариями, сценарий их игнорирует.
__.__..___
___._.____
____.___..
_._______.
____._____
..__...___
____._____
___...____
__.._..___
_..___..__
+++
Следующие два сценария предоставил Mark Moraes, из университета в Торонто. См. файл "Moraes-COPYRIGHT", который содержит указание на авторские права.
Пример A-13. behead: Удаление заголовков из электронных писем и новостей
#! /bin/sh
# Удаление заголовков из электронных писем и новостей т.е. до первой
# пустой строки
# Mark Moraes, Университет в Торонто
# ==> Такие комментарии добавлены автором документа.
if [ $# -eq 0 ]; then
# ==> Если входной аргумент не задан (файл), то выводить результат на stdin.
sed -e '1,/^$/d' -e '/^[ ]*$/d'
# --> Удалить пустые строки и все строки предшествующие им
else
# ==> Если аргумент командной строки задан, то использовать его как имя файла.
for i do
sed -e '1,/^$/d' -e '/^[ ]*$/d' $i
# --> То же, что и выше.
done
fi
# ==> Упражнение: Добавьте проверку на наличие ошибок.
# ==>
# ==> Обратите внимание -- как похожи маленькие сценарии sed, за исключением передачи аргумента.
# ==> Можно ли его оформит в виде функции? Почему да или почему нет?
Пример A-14. ftpget: Скачивание файлов по ftp
#! /bin/sh
# $Id: ftpget,v 1.2 91/05/07 21:15:43 moraes Exp $
# Сценарий устанавливает анонимное соединение с ftp-сервером.
# Простой и быстрый - написан как дополнение к ftplist
# -h -- удаленный сервер (по-умолчанию prep.ai.mit.edu)
# -d -- каталог на сервере - вы можете указать последовательность из нескольких ключей -d
# Если вы используете относительные пути,
# будьте внимательны при задании последовательности.
# (по-умолчанию -- каталог пользователя ftp)
# -v -- "многословный" режим, будет показывать все ответы ftp-сервера
# -f -- file[:localfile] скачивает удаленный file и записывает под именем localfile
# -m -- шаблон для mget. Не забудьте взять в кавычки!
# -c -- локальный каталог
# Например,
# ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh
# -d ../pub/R3/fixes -c ~/fixes -m 'fix*'
# Эта команда загрузит файл xplaces.shar из ~ftp/contrib с expo.lcs.mit.edu
# и сохранит под именем xplaces.sh в текущем каталоге, затем заберет все исправления (fixes)
# из ~ftp/pub/R3/fixes и поместит их в каталог ~/fixes.
# Очевидно, что последовательность ключей и аргументов очень важна, поскольку
# она определяет последовательность операций, выполняемых с удаленным ftp-сервером
#
# Mark Moraes ([email protected]), Feb 1, 1989
#
# ==> Эти комментарии добавлены автором документа.
# PATH=/local/bin:/usr/ucb:/usr/bin:/bin
# export PATH
# ==> Первые две строки в оригинальном сценарии вероятно излишни.
TMPFILE=/tmp/ftp.$$
# ==> Создан временный файл
SITE=`domainname`.toronto.edu
# ==> 'domainname' подобен 'hostname'
usage="Порядок использования: $0 [-h удаленный_сервер] [-d удаленный_каталог]... [-f удаленный_файл:локальный_файл]...
[-c локальный_каталог] [-m шаблон_имен_файлов] [-v]"
ftpflags="-i -n"
verbflag=
set -f # разрешить подстановку имен файлов (globbing) для опции -m
set x `getopt vh:d:c:m:f: $*`
if [ $? != 0 ]; then
echo $usage
exit 65
fi
shift
trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15
echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}"
# ==> Добавлены кавычки (рекомендуется).
echo binary >> ${TMPFILE}
for i in $* # ==> Разбор командной строки.
do
case $i in
-v) verbflag=-v; echo hash >> ${TMPFILE}; shift;;
-h) remhost=$2; shift 2;;
-d) echo cd $2 >> ${TMPFILE};
if [ x${verbflag} != x ]; then
echo pwd >> ${TMPFILE};
fi;
shift 2;;
-c) echo lcd $2 >> ${TMPFILE}; shift 2;;
-m) echo mget "$2" >> ${TMPFILE}; shift 2;;
-f) f1=`expr "$2" : "([^:]*).*"`; f2=`expr "$2" : "[^:]*:(.*)"`;
echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;;
--) shift; break;;
esac
done
if [ $# -ne 0 ]; then