#set text(size: 1.3em)

#show raw.where(block: false): box.with(
  fill: luma(240),
  inset: (x: 3pt, y: 0pt),
  outset: (y: 3pt),
  radius: 2pt,
)

#show raw.where(block: true): block.with(
  fill: luma(240),
  inset: 10pt,
  radius: 4pt,
)
    
// title
    
#align(center)[Санкт-Петербургский национальный исследовательский университет информационных технологий, механики и оптики]
\
\
\
#align(center)[Факультет инфокоммуникационных технологий]
#align(center)[Направление подготовки 11.03.02]
\
\
#align(center)[Лабораторная работа №2]
#align(center)[Создание и использование размерных типов данных]
\
\
\ //#align(center)[Вариант 19]
\
\
\
\
\
\
\
#align(right)[Выполнил:]
#align(right)[Дощенников Никита Андреевич]
#align(right)[Группа: К3221]
#align(right)[Проверил:]
#align(right)[Береснев Артем Дмитриевич]
\
\
#align(center)[Санкт-Петербург]
#align(center)[2025]

#pagebreak()
    
=== Цель работы: 

Получить практические навыки программирования на bash, освоить практические приемы работы с командной оболочкой bash.

=== Часть 1. Работа в среде bash.

Для изменения приглашения командной строки я отредактировал файл `~/.bashrc`, добавив туда строку:

```bash
PS1='\t \u \w \$ '
```

Теперь приглашение отображает текущее время, имя пользователя, текущий каталог и символ `$`.

#align(center)[#image("assets/1.png")]

Создал алиас для вывода приветствия:

```bash
alias hi='echo "Have a nice day, $(whoami)!"'
```

После этого при вводе команды `hi` в терминале появляется строка с приветствием и именем текущего пользователя.

#align(center)[#image("assets/2.png")]

Чтобы изменения сохранились при перезапуске, эти строки были добавлены в файл `~/.bashrc`. Применил изменения без выхода из системы командой:

```bash
source ~/.bashrc
```

=== Часть 2. Простые задачи на bash.

1. Принимает две строки как параметры и сравнивает их.

```bash
#!/bin/bash
[ "$#" -ne 2 ] && echo "usage: $0 str1 str2" && exit 1
[ "$1" = "$2" ] && echo "equal" || echo "not equal"
```

#align(center)[#image("assets/3.png")]

2. Принимает три целых числа и находит максимальное из них.

```bash
#!/bin/bash
[ "$#" -ne 3 ] && echo "usage: $0 a b c" && exit 1
a=$1
b=$2
c=$3
max=$a
(( b > max )) && max=$b
(( c > max )) && max=$c
echo "$max"
```

#align(center)[#image("assets/4.png")]

3. Считывает строки до тех пор, пока не встретится строка `q`. После завершения цикла все введённые строки объединяются и выводятся в одну строку.

```bash
#!/bin/bash
arr=()
while IFS= read -r line; do
  [ "$line" = "q" ] && break
  arr+=("$line")
done
printf "%s" "${arr[*]}"
```

#align(center)[#image("assets/5.png")]

4. Считывает последовательность целых чисел. Чтение продолжается, пока не встретится чётное число. После этого выводится количество введённых чисел до него.

```bash
#!/bin/bash
count=0
while IFS= read -r n; do
  r=$(( n % 2 ))
  [ "$r" -eq 0 ] && break
  count=$(( count + 1 ))
done
echo "$count"
```

#align(center)[#image("assets/6.png")]

5. Создано текстовое меню с четырьмя пунктами: запуск редактора `nano`, редактора `vi`, браузера `links`, или выход. Перед выполнением команда `command -v` проверяет наличие программ. Если программа не установлена — выводится сообщение и скрипт завершается.

```bash
#!/bin/bash
for cmd in nano vi links; do
  command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is not installed"; exit 1; }
done
echo "1) nano"
echo "2) vi"
echo "3) links"
echo "4) exit"
read -r -p "choose 1-4: " choice
case "$choice" in
  1) exec nano ;;
  2) exec vi ;;
  3) exec links ;;
  4) exit 0 ;;
  *) echo "invalid choice" ; exit 1 ;;
esac
```

#align(center)[#image("assets/7.png")]
#align(center)[#image("assets/8.png")]

6. Принимает три параметра: имя файла, режим (`h` или `t`), и число строк. В режиме `h` печатаются первые N строк файла (`head -n`). В режиме `t` — последние N строк (`tail -n`). Ошибки при отсутствии файла подавляются перенаправлением `2>/dev/null`.

```bash
#!/bin/bash
[ "$#" -ne 3 ] && echo "usage: $0 file h|t N" && exit 1
file=$1
mode=$2
num=$3
case "$mode" in
  h) head -n "$num" -- "$file" 2>/dev/null ;;
  t) tail -n "$num" -- "$file" 2>/dev/null ;;
  *) echo "mode must be h or t" ; exit 1 ;;
esac
```

#align(center)[#image("assets/6.png")]

=== Часть 3. Разработка скрипта расчета производительности.

1. В этой части требовалось написать скрипт `script23`, который измеряет производительность CPU в FLOPS (Floating Point Operations Per Second). Для этого необходимо было реализовать большое количество операций с плавающей запятой и зафиксировать время их выполнения.

2. Для вычислений использовалась утилита `bc`, так как она позволяет работать с произвольной точностью. В скрипте выставлена точность `scale=10`.

3. В скрипте организована функция `bench`, в которой выполняется $N$ умножений двух одинаковых чисел. Параметр $N$ можно передать в скрипт, по умолчанию используется 1 000 000.

4. С помощью команды `date +%s.%N` время сохраняется до и после выполнения функции. Далее определяется разница во времени и вычисляется показатель FLOPS по формуле:

$
F L O P S = frac(N, Delta t)
$

5. В завершении программа выводит три значения: количество итераций, время выполнения в секундах, и рассчитанный FLOPS.

```bash
#!/bin/bash
n=${1:-1000000}
command -v bc >/dev/null 2>&1 || { echo "bc is required"; exit 1; }

bench() {
  printf 'scale=10\na=1.2345678901\nb=1.2345678901\nfor(i=0;i<%s;i++) x=a*b\n' "$n" | bc >/dev/null
}

start=$(date +%s.%N)
bench
end=$(date +%s.%N)

dur=$(echo "$end - $start" | bc -l)
flops=$(echo "$n / $dur" | bc -l)

printf "iterations=%s\ntime_s=%s\nFLOPS=%s\n" "$n" "$dur" "$flops"
```

#align(center)[#image("assets/10.png")]

=== Вопросы.

==== 1. Как определить сколько параметров пользователь передал в скрипт при запуске?

В bash для этого используется специальная переменная `$#`.
Она хранит количество аргументов, переданных скрипту.
Пример:

```bash
echo "amount of params: $#"
```

==== 2. Что выведет простой скрипт с постдекрементом?

Код:

```bash
count=10
echo $(( count-- ))
echo $(( count-- ))
```

#align(center)[#image("assets/11.png")]

Первая строка напечатает `10`, потому что оператор постдекремента возвращает старое значение, а потом уменьшает переменную. После этой операции `count=9`. Вторая строка напечатает `9` — аналогично.

==== 3. Недостатки подхода к определению FLOPS в нашем скрипте

- Измеряется не только операция умножения, но и работа интерпретатора bash и программы `bc`.
- Не используется SIMD (векторные инструкции) и многопоточность, поэтому результат сильно занижен.
- Выполняется только умножение одинаковых чисел, что не отражает реальную работу CPU.
- На результат влияют другие процессы в системе, частота процессора, троттлинг, кэш.
- Такой метод больше демонстрационный и не сопоставим с эталонными тестами на C/Fortran.

=== Вывод.

В ходе проделанной работы я получил практические навыки программирования на bash, освоил практические приемы работы с командной оболочкой bash.

