БAL Лаб4
.docxМИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ
УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»
(СПбГУТ)
Факультет Кибербезопасности
Кафедра Защищенных систем связи
Дисциплина Безопасность Astra-Linux
ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ №4
Введение в язык командного интерпретатора Bash
(тема работы)
Направление/специальность подготовки
10.05.02 «Информационная безопасность телекоммуникационных систем»
(код и наименование направления/специальности)
Студенты:
(Ф.И.О., № группы) (подпись)
(Ф.И.О., № группы) (подпись)
Преподаватель:
ст.преп. Гельфанд А.М.
(уч. степень, уч. звание, Ф.И.О.) (подпись)
ТЕКСТ ЗАДАНИЯ
Цель лабораторной работы
Ознакомится с языком одной из самых популярных командных оболочек
Linux– bash(Bourneagainshell).
Изучить создание переменных, организацию циклов и ветвлений.
Освоить работу с массивами и передачу аргументов в программу.
Освоить ввод данных в программу с клавиатуры.
Получить навык написания простейших скриптов.
Используемое программное обеспечение
Для выполнения лабораторной работы используется установленный
дистрибутив ОС Linux с установленным bash (является стандартным в
большинстве дистрибутивов).
ХОД РАБОТЫ
1. Создали файл скрипта и написали код с помощью команды nano «lab4.sh» (рис. 1).
Рис.1 – Текст программы
2. Сделали файл исполняемым благодаря команде «chmod +x lab4.sh».
3. Проверили работу программы (рис. 2, рис. 3).
Рис. 2 – Игровой процесс
Рис. 3 – Конец игры
ЛИСТИНГ ПРОГРАММЫ
#!/usr/bin/env bash
# Объявление основных переменных игры
declare -ia board # игровое поле как массив чисел
declare -i pieces # количество заполненных ячеек
declare -i score=0 # счет игрока
declare -i flag_skip # флаг для предотвращения многократных сдвигов
declare -i moves # счетчик возможных ходов
declare ESC=$'\e' # escape-символ для управления курсором
# Настройки игры по умолчанию
declare -i board_size=4 # размер поля 4x4
declare -i target=2048 # целевое число для победы
# Цвета для отображения чисел
declare -a colors
colors[2]=33 # желтый
colors[4]=32 # зеленый
colors[8]=34 # синий
colors[16]=36 # голубой
colors[32]=35 # фиолетовый
colors[64]="33m\033[7" # желтый фон
colors[128]="32m\033[7" # зеленый фон
colors[256]="34m\033[7" # синий фон
colors[512]="36m\033[7" # голубой фон
colors[1024]="35m\033[7" # фиолетовый фон
colors[2048]="31m\033[7" # красный фон
exec 3>/dev/null # отключение логирования по умолчанию
trap "end_game 0 1" INT # обработка прерывания Ctrl+C
# Функция для генерации последовательности чисел (замена seq)
function _seq {
local cur=1
local max
local inc=1
case $# in
1) let max=$1;;
2) let cur=$1
let max=$2;;
3) let cur=$1
let inc=$2
let max=$3;;
esac
while test $max -ge $cur; do
printf "$cur "
let cur+=inc
done
}
# Функция отрисовки игрового поля
function print_board {
clear # очистка экрана
printf "Board status:\n" >&3 # логирование в файл
printf "\n"
# Отрисовка верхней границы поля
printf '/------'
for l in $(_seq 1 $index_max); do
printf '+------'
done
printf '\\\n'
# Отрисовка строк с числами
for l in $(_seq 0 $index_max); do
printf '|'
for m in $(_seq 0 $index_max); do
if let ${board[l*$board_size+m]}; then # если ячейка не пустая
if let '(last_added==(l*board_size+m))|(first_round==(l*board_size+m))'; then
# Красный цвет для новых плиток
printf '\033[1m\033[31m %4d \033[0m|' ${board[l*$board_size+m]}
else
# Цвет в зависимости от значения
printf "\033[1m\033[${colors[${board[l*$board_size+m]}]}m %4d\033[0m |" ${board[l*$board_size+m]}
fi
printf " %4d |" ${board[l*$board_size+m]} >&3 # логирование
else
printf ' |' # пустая ячейка
printf ' |' >&3
fi
done
let l==$index_max || {
# Разделитель между строками
printf '\n|------'
for l in $(_seq 1 $index_max); do
printf '+------'
done
printf '|\n'
printf '\n' >&3
}
done
# Нижняя граница поля
printf '\n\\------'
for l in $(_seq 1 $index_max); do
printf '+------'
done
printf '/\n'
}
# Генерация новой плитки на поле
function generate_piece {
while true; do
let pos=RANDOM%fields_total # случайная позиция
let board[$pos] || { # если ячейка пустая
let value=RANDOM%10?2:4 # 90% - 2, 10% - 4
board[$pos]=$value
last_added=$pos # запоминаем позицию новой плитки
printf "Generated new piece with value $value at position [$pos]\n" >&3
break;
}
done
let pieces++ # увеличиваем счетчик плиток
}
# Функция сдвига плиток в заданном направлении
function push_pieces {
case $4 in # определение направления
"up") # вверх
let "first=$2*$board_size+$1"
let "second=($2+$3)*$board_size+$1"
;;
"down") # вниз
let "first=(index_max-$2)*$board_size+$1"
let "second=(index_max-$2-$3)*$board_size+$1"
;;
"left") # влево
let "first=$1*$board_size+$2"
let "second=$1*$board_size+($2+$3)"
;;
"right") # вправо
let "first=$1*$board_size+(index_max-$2)"
let "second=$1*$board_size+(index_max-$2-$3)"
;;
esac
let ${board[$first]} || { # если первая ячейка пустая
let ${board[$second]} && { # и вторая не пустая
if test -z $5; then # если не фейковый ход
board[$first]=${board[$second]} # перемещаем плитку
let board[$second]=0
let change=1 # отмечаем изменение
printf "move piece with value ${board[$first]} from [$second] to [$first]\n" >&3
else
let moves++ # увеличиваем счетчик возможных ходов
fi
return
}
return
}
let ${board[$second]} && let flag_skip=1 # блокировка дальнейших сдвигов
let "${board[$first]}==${board[second]}" && { # если значения равны
if test -z $5; then # если не фейковый ход
let board[$first]*=2 # объединяем плитки
let "board[$first]==$target" && end_game 1 # проверка победы
let board[$second]=0
let pieces-=1 # уменьшаем счетчик плиток
let change=1 # отмечаем изменение
let score+=${board[$first]} # увеличиваем счет
printf "joined piece from [$second] with [$first], new value=${board[$first]}\n" >&3
else
let moves++ # увеличиваем счетчик возможных ходов
fi
}
}
# Применение сдвига ко всему полю
function apply_push {
printf "\n\ninput: $1 key\n" >&3
for i in $(_seq 0 $index_max); do
for j in $(_seq 0 $index_max); do
flag_skip=0
let increment_max=index_max-j
for k in $(_seq 1 $increment_max); do
let flag_skip && break # если сдвиг заблокирован
push_pieces $i $j $k $1 $2 # сдвиг плитки
done
done
done
}
# Проверка доступных ходов
function check_moves {
let moves=0
# Проверяем все направления
apply_push up fake
apply_push down fake
apply_push left fake
apply_push right fake
}
# Обработка нажатий клавиш
function key_react {
let change=0 # сбрасываем флаг изменений
read -d '' -sn 1 # чтение одного символа
test "$REPLY" = "$ESC" && { # если нажата ESC
read -d '' -sn 1 -t1
test "$REPLY" = "[" && { # стрелки
read -d '' -sn 1 -t1
case $REPLY in
A) apply_push up;; # стрелка вверх
B) apply_push down;; # стрелка вниз
C) apply_push right;; # стрелка вправо
D) apply_push left;; # стрелка влево
esac
}
} || { # обычные клавиши
case $REPLY in
k) apply_push up;; # vim-стиль
j) apply_push down;;
l) apply_push right;;
h) apply_push left;;
w) apply_push up;; # wasd-стиль
s) apply_push down;;
d) apply_push right;;
a) apply_push left;;
esac
}
}
# Завершение игры
function end_game {
print_board # показываем финальное состояние
stty echo # включаем отображение ввода
printf "Game over\n" # сообщение о завершении
exit 0
}
# Функция помощи
function help {
cat <<END_HELP
Usage: $1 [-b INTEGER] [-t INTEGER] [-l FILE] [-h]
-b размер игрового поля (от 3 до 9)
-t целевое число для победы (степень двойки)
-l файл для логирования отладочной информации
-h показать эту справку
END_HELP
}
# Парсинг аргументов командной строки
while getopts "b:t:l:h" opt; do
case $opt in
b ) board_size="$OPTARG" # установка размера поля
let '(board_size>=3)&(board_size<=9)' || {
printf "Invalid board size, please choose size between 3 and 9\n"
exit -1
};;
t ) target="$OPTARG" # установка целевого числа
printf "obase=2;$target\n" | bc | grep -e '^1[^1]*$' # проверка что это степень двойки
let $? && {
printf "Invalid target, has to be power of two\n"
exit -1
};;
h ) help $0 # показать справку
exit 0;;
l ) exec 3>$OPTARG;; # включить логирование в файл
\?) printf "Invalid option: -"$opt", try $0 -h\n" >&2
exit 1;;
: ) printf "Option -"$opt" requires an argument, try $0 -h\n" >&2
exit 1;;
esac
done
# Инициализация игрового поля
let fields_total=board_size*board_size # общее количество ячеек
let index_max=board_size-1 # максимальный индекс
for i in $(_seq 0 $fields_total); do board[$i]="0"; done # заполняем нулями
let pieces=0 # обнуляем счетчик плиток
generate_piece # генерируем первую плитку
first_round=$last_added # запоминаем позицию первой плитки
generate_piece # генерируем вторую плитку
# Главный игровой цикл
while true; do
print_board # отрисовываем поле
key_react # обрабатываем ввод
let change && generate_piece # если было изменение - генерируем новую плитку
first_round=-1 # сбрасываем флаг первого раунда
let pieces==fields_total && { # если поле заполнено
check_moves # проверяем возможные ходы
let moves==0 && end_game 0 # если ходов нет - завершаем игру
}
done
ВЫВОДЫ
В результате выполнения работы изучены основы bash: переменные, циклы, ввод/вывод, реализована программа-игра 2048, получен навык написания и запуска bash-скриптов в Astra Linux.
