Мендель Купер - Искусство программирования на языке сценариев командной оболочки
#
# Этот модифицированный вариант включен в документ на основе
#+ документа "LICENSE" из пакета "Crack"
#+ с которым распространяется оригинальный сценарий.
# Этот скрипт обрабатывает текстовые файлы и создает отсортированный список
#+ слов, найденных в этих файлах.
# Он может оказаться полезным для сборки словарей
#+ и проведения лексикографического анализа.
E_BADARGS=65
if [ ! -r "$1" ] # Необходим хотя бы один аргумент --
then #+ имя файла.
echo "Порядок использования: $0 имена_файлов"
exit $E_BADARGS
fi
# SORT="sort" # Необходимость задания ключей сортировки отпала.
#+ Изменено, по отношению к оригинальному сценарию.
cat $* | # Выдать содержимое файлов на stdout.
tr A-Z a-z | # Преобразовать в нижний регистр.
tr ' ' ' 12' | # Новое: заменить пробелы символами перевода строки.
# tr -cd ' 12[a-z][0-9]' | # В оригинальном сценарии: удалить все символы,
#+ которые не являются буквами или цифрами.
tr -c ' 12a-z' ' 12' | # Вместо удаления
#+ неалфавитно-цифровые символы заменяются на перевод строки.
sort |
uniq | # Удалить повторяющиеся слова.
grep -v '^#' | # Удалить строки, начинающиеся с "#".
grep -v '^$' # Удалить пустые строки.
exit 0
Пример A-10. Расчет индекса "созвучности"
#!/bin/bash
# soundex.sh: Расчет индекса "созвучности"
# =======================================================
# Сценарий Soundex
# Автор
# Mendel Cooper
# [email protected]
# 23 Января 2002 г.
#
# Условия распространения: Public Domain.
#
# Несколько отличающаяся версия этого сценария была опубликована
#+ Эдом Шэфером (Ed Schaefer) в Июле 2002 года в колонке "Shell Corner"
#+ "Unix Review" on-line,
#+ http://www.unixreview.com/documents/uni1026336632258/
# =======================================================
ARGCOUNT=1 # Требуется аргумент командной строки.
E_WRONGARGS=70
if [ $# -ne "$ARGCOUNT" ]
then
echo "Порядок использования: `basename $0` имя"
exit $E_WRONGARGS
fi
assign_value () # Присвоить числовые значения
{ #+ символам в имени.
val1=bfpv # 'b,f,p,v' = 1
val2=cgjkqsxz # 'c,g,j,k,q,s,x,z' = 2
val3=dt # и т.п.
val4=l
val5=mn
val6=r
# Попробуйте разобраться в том, что здесь происходит.
value=$( echo "$1"
| tr -d wh
| tr $val1 1 | tr $val2 2 | tr $val3 3
| tr $val4 4 | tr $val5 5 | tr $val6 6
| tr -s 123456
| tr -d aeiouy )
# Символам в имени присваиваются числовые значения.
# Удаляются повторяющиеся числа, если они не разделены гласными.
# Гласные игнорируются, если они не являются разделителями, которые удаляются в последнюю очередь.
# Символы 'w' и 'h' удаляются в первую очередь.
}
input_name="$1"
echo
echo "Имя = $input_name"
# Перевести все символы в имени в нижний регистр.
# ------------------------------------------------
name=$( echo $input_name | tr A-Z a-z )
# ------------------------------------------------
# Начальный символ в индекса "созвучия": первая буква в имени.
# --------------------------------------------
char_pos=0 # Начальная позиция в имени.
prefix0=${name:$char_pos:1}
prefix=`echo $prefix0 | tr a-z A-Z`
# Первую букву в имени -- в верхний регистр.
let "char_pos += 1" # Передвинуть "указатель" на один символ.
name1=${name:$char_pos}
# ++++++++++++++++++++++++++++ Исключение отдельных ситуаций +++++++++++++++++++++++++++++++
# Теперь мы передвинулись на один символ вправо.
# Если второй символ в имени совпадает с первым
#+ то его нужно отбросить.
# Кроме того, мы должны проверить -- не является ли первый символ
#+ гласной, 'w' или 'h'.
char1=`echo $prefix | tr A-Z a-z` # Первый символ -- в нижний регистр.
assign_value $name
s1=$value
assign_value $name1
s2=$value
assign_value $char1
s3=$value
s3=9$s3 # Если первый символ в имени -- гласная буква
#+ или 'w' или 'h',
#+ то ее "значение" нужно отбросить.
#+ Поэтому ставим 9, или другое
#+ неиспользуемое значение, которое можно будет проверить.
if [[ "$s1" -ne "$s2" || "$s3" -eq 9 ]]
then
suffix=$s2
else
suffix=${s2:$char_pos}
fi
# ++++++++++++++++++++++++ Конец исключения отдельных ситуаций +++++++++++++++++++++++++++++++
padding=000 # Дополнить тремя нулями.
soun=$prefix$suffix$padding # Нули добавить в конец получившегося индекса.
MAXLEN=4 # Ограничить длину индекса 4-мя символами.
soundex=${soun:0:$MAXLEN}
echo "Индекс созвучия = $soundex"
echo
# Индекс "созвучия" - это метод индексации и классификации имен
#+ по подобию звучания.
# Индекс "созвучия" начинается с первого символа в имени,
#+ за которым следуют 3-значный расчетный код.
# Имена, которые произносятся примерно одинаково, имеют близкие индексы "созвучия".
# Например:
# Smith и Smythe -- оба имеют индекс "созвучия" "S530".
# Harrison = H625
# Hargison = H622
# Harriman = H655
# Как правило эта методика дает неплохой результат, но имеются и аномалии.
#
#
# Дополнительную информацию вы найдете на
#+ "National Archives and Records Administration home page",
#+ http://www.nara.gov/genealogy/soundex/soundex.html
# Упражнение:
# ----------
# Упростите блок "Исключение отдельных ситуаций" .
exit 0
Пример A-11. "Игра "Жизнь""
#!/bin/bash
# life.sh: Игра "Жизнь"
# ##################################################################### #
# Это Bash-версия известной игры Джона Конвея (John Conway) "Жизнь". #
# --------------------------------------------------------------------- #
# Прямоугольное игровое поле разбито на ячейки, в каждой ячейке может #
#+ располагаться живая особь. #
# Соответственно, ячейка с живой особью отмечается точкой, #
#+ не занятая ячейка -- остается пустой. #
# Изначально, ячейки заполняются из файла -- #
#+ это первое поколение, или "поколение 0" #
# Воспроизводство особей, в каждом последующем поколении, #
#+ определяется следующими правилами #
# 1) Каждая ячейка имеет "соседей" #
#+ слева, справа, сверху, снизу и 4 по диагоналям. #
# 123 #
# 4*5 #
# 678 #
# #
# 2) Если живая особь имеет 2 или 3 живых соседей, то она остается жить.#
# 3) Если пустая ячейка имеет 3 живых соседей -- #
#+ в ней "рождается" новая особь #
SURVIVE=2 #
BIRTH=3 #
# 4) В любом другом случае, живая особь "погибает" #
# ##################################################################### #
startfile=gen0 # Начальное поколение из файла по-умолчанию -- "gen0".
# если не задан другой файл, из командной строки.
#
if [ -n "$1" ] # Проверить аргумент командной строки -- файл с "поколениемn 0".
then
if [ -e "$1" ] # Проверка наличия файла.
then
startfile="$1"
fi
fi
ALIVE1=.
DEAD1=_
# Представление "живых" особей и пустых ячеек в файле с "поколением 0".
# Этот сценарий работает с игровым полем 10 x 10 grid (может быть увеличено,
#+ но большое игровое поле будет обрабатываться очень медленно).
ROWS=10
COLS=10
GENERATIONS=10 # Максимальное число поколений.
NONE_ALIVE=80 # Код завершения на случай,
#+ если не осталось ни одной "живой" особи.
TRUE=0
FALSE=1
ALIVE=0
DEAD=1
avar= # Текущее поколение.
generation=0 # Инициализация счетчика поколений.
# =================================================================
let "cells = $ROWS * $COLS"
# Количество ячеек на игровом поле.
declare -a initial # Массивы ячеек.
declare -a current
display ()
{
alive=0 # Количество "живых" особей.
# Изначально -- ноль.
declare -a arr
arr=( `echo "$1"` ) # Преобразовать аргумент в массив.
element_count=${#arr[*]}
local i
local rowcheck
for ((i=0; i<$element_count; i++))
do
# Символ перевода строки -- в конец каждой строки.
let "rowcheck = $i % ROWS"
if [ "$rowcheck" -eq 0 ]
then
echo # Перевод строки.
echo -n " " # Выравнивание.
fi
cell=${arr[i]}
if [ "$cell" = . ]
then
let "alive += 1"
fi
echo -n "$cell" | sed -e 's/_/ /g'
# Вывести массив, по пути заменяя символы подчеркивания на пробелы.
done
return
}
IsValid () # Проверка корректности координат ячейки.
{
if [ -z "$1" -o -z "$2" ] # Проверка наличия входных аргументов.
then
return $FALSE
fi
local row
local lower_limit=0 # Запрет на отрицательные координаты.
local upper_limit
local left
local right
let "upper_limit = $ROWS * $COLS - 1" # Номер последней ячейки на игровом поле.