|
|
# Задания
|
|
|
|
|
|
Сценарии оболочки — это следующий шаг в сложности комбинирования команд после конвейеров. Большинство оболочек имеют собственный язык сценариев с переменными, потоком управления и собственным синтаксисом. Сценарии оболочки отличаются от других языков программирования сценариев тем, что они оптимизированы для выполнения задач, связанных с оболочкой. Таким образом, создание конвейеров команд, сохранение результатов в файлы и чтение из стандартного ввода являются примитивами в сценариях оболочки, что делает их более простыми в использовании, чем языки сценариев общего назначения. Далее задания посвящены сценариям bash, поскольку они наиболее распространены.
|
|
|
|
|
|
### 1.
|
|
|
Чтобы назначить переменные в bash, используйте синтаксис `foo=bar`. Чтобы получить доступ к значению переменной запишите знак `$` перед её именем `$foo`. Обратите внимание, что `foo = bar` не будет работать, так как интерпретируется как вызов программы `foo` с аргументами `=` и `bar`. Как правило, в сценариях оболочки символ пробела выполняет разделение аргументов. Поначалу такое поведение может сбивать с толку, поэтому всегда проверяйте его.
|
|
|
|
|
|
Строки в bash могут быть определены парными символами `'` или `"`, но они не эквивалентны. Строки, разделенные `'`, являются литеральными строками и не интерпретируют bash выражения, тогда как строки с `"` будут. Для экранирования кавычек используется символ обратного слеша `\`.
|
|
|
|
|
|
Напишите .sh скрипт в котором в переменную date присваивается значение текущей даты, в переменную claim - строка "snow is white". После вызова скрипт должен выводить строку, используя переменные date и claim:
|
|
|
```
|
|
|
Ср 14 сен 2022 14:50:46 +04: The claim that “snow is white” is true if and only if snow is white.
|
|
|
```
|
|
|
|
|
|
Сделайте файл исполняемым для владельца и вызовите указанием полного или относительного пути к нему.
|
|
|
|
|
|
### 2.
|
|
|
Как и в большинстве языков программирования, bash поддерживает методы управления потоком, включая условные и циклические операторы if, case, while и for. Точно так же в bash есть функции, которые принимают аргументы и могут с ними работать. Вот пример определения функции:
|
|
|
```
|
|
|
echo_arguments () {
|
|
|
echo "First three arguments: $0 $1 $2 $3, Argument count: $#, All arguments: $@"
|
|
|
}
|
|
|
```
|
|
|
В отличие от других языков сценариев, bash использует множество специальных переменных для ссылки на аргументы, коды ошибок и другие важные переменные. Ниже приведен список некоторых из них. Более полный список можно найти здесь (https://tldp.org/LDP/abs/html/special-chars.html или https://www.opennet.ru/docs/RUS/bash_scripting_guide/c301.html.
|
|
|
|
|
|
`$0` - имя скрипта, `$1` до `$9` - аргументы скрипта, `$@` - все аргументы, `$#` - количество аргументов,`$?` - возвращаемый код предыдущей команды, `$$` - идентификатор процесса текущего скрипта, `!!` - вся команда, включая аргументы (попробуйте `sudo !!`, если забыли, что команде требуются права суперпользователя), `$_` - последний аргумент последней команды (в терминале попробуйте нажать `Esc .` или `Alt+`).
|
|
|
|
|
|
Напишите функцию в скрипте, которая создает каталог, путь к которому передан в качестве первого аргумента и переходит в него. Проверьте работу функции в этом же файле. Работает ли функция за пределами контекста скрипта?
|
|
|
|
|
|
### 3.
|
|
|
Менее известной, но полезной может быть подстановка дескриптора файла с результатом выполнения команды. `<( CMD )` выполнит `CMD` и поместит вывод во временный файл и заменит `<()` именем этого файла. Это полезно, когда команды ожидают, что значения будут переданы через файл, а не STDIN. Например, `diff <(ls foo) <(ls bar)` покажет различия между файлами в каталогах foo и bar.
|
|
|
|
|
|
### 4.
|
|
|
Команды часто возвращают выходные данные, используя `STDOUT`, ошибки — через `STDERR` и код возврата, чтобы сообщать об ошибках более удобным для сценариев способом. Код возврата или статус выхода — это то, как скрипты/команды должны сообщать о том, как прошло выполнение. Значение 0 обычно означает, что все прошло нормально; все, что отличается от 0, означает, что произошла ошибка.
|
|
|
|
|
|
Коды выхода можно использовать для условного выполнения команд с использованием `&&` (и оператора) и `||` (или оператор), оба из которых являются операторами. Команды также можно разделять в пределах одной строки с помощью точки с запятой `;`. Истинная программа всегда будет иметь код возврата `0`, а ложная команда всегда будет иметь код возврата `1`.
|
|
|
|
|
|
Попробуйте выполнить следующие примеры:
|
|
|
```
|
|
|
false || echo "Oops, fail"
|
|
|
# Oops, fail
|
|
|
|
|
|
true || echo "Will not be printed"
|
|
|
#
|
|
|
|
|
|
true && echo "Things went well"
|
|
|
# Things went well
|
|
|
|
|
|
false && echo "Will not be printed"
|
|
|
#
|
|
|
|
|
|
true ; echo "This will always run"
|
|
|
# This will always run
|
|
|
|
|
|
false ; echo "This will always run"
|
|
|
# This will always run
|
|
|
```
|
|
|
|
|
|
Попробуйте условные выражения в скрипте в if конструкции:
|
|
|
```
|
|
|
if [[ условное выражение ]]; then
|
|
|
echo "True"
|
|
|
fi
|
|
|
```
|
|
|
|
|
|
### 5.
|
|
|
Напишите скрипт, который проходит по всем файлам, переданным в качестве аргументов, и ищет в них строку hello. Перенаправьте grep `STDOUT` и `STDERR` в специальный файл `/dev/null`. Для каждого файла в if создайте файл с содержимым `hello`, если `grep` завершился с ошибкой.
|
|
|
|
|
|
### 6.
|
|
|
Установите пакет shellcheck и проверьте написанный вами ранее скрипт одноименной командой.
|
|
|
|
|
|
### 7.
|
|
|
Cкрипты не обязательно должны быть написаны на bash для вызова из терминала. Ядро определяет, что сценарий нужно выполнять с помощью другого интерпретатора вместо командой оболочки, если в начало скрипта включена строка shebang. Хорошей практикой является написание строк shebang с помощью команды env, которая будет разрешаться везде, где эта команда находится в системе, что повышает переносимость ваших сценариев. Например, чтобы определить местоположение python, env будет использовать переменную среды `PATH` при записи `#!/usr/bin/env python`.
|
|
|
|
|
|
Напишите скрипт, который выполняет следующий код:
|
|
|
```
|
|
|
import sys
|
|
|
for arg in reversed(sys.argv[1:]):
|
|
|
print(arg)
|
|
|
```
|
|
|
|
|
|
### 8.
|
|
|
Функции выполняются в текущей среде оболочке, тогда как скрипты выполняются в своем собственном процессе.
|
|
|
|
|
|
Проверьте, что функции могут изменять переменные среды, например, изменить текущий каталог, тогда как скрипт не может. Напишите скрипт, в котором инициализируются две переменные `A` и `B`. Перед одной из них указывается `export `. Выполните скрипт и посмотрите на выдачу команды `env`. Выполните файл командой source (source --help). Какие переменные экспортировались в оболочку?
|
|
|
|
|
|
### 9.
|
|
|
Скажем, у вас есть команда, которая редко дает сбой. Чтобы отладить её, вам нужно зафиксировать её выходные данные, но это может занять много времени, чтобы поймать неудачный запуск. Напишите сценарий bash, который запускает следующий сценарий до тех пор, пока он не завершится аварийно, записывает стандартный вывод и потоки ошибок в файлы и печатает все в конце. Бонусный балл, за то, что вы можете сообщить, сколько запусков потребовалось для сбоя сценария.
|
|
|
```
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
n=$(( RANDOM % 100 ))
|
|
|
|
|
|
if [[ n -eq 42 ]]; then
|
|
|
echo "Something went wrong"
|
|
|
>&2 echo "The error was using magic numbers"
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
echo "Everything went according to plan"
|
|
|
```
|
|
|
|
|
|
### 10.
|
|
|
Напишите скрипт, который:
|
|
|
- создаёт пользователя на двух машинах,
|
|
|
- кроме домашней директории заводит папку для пользователя в `/ceph/` с нужными правами,
|
|
|
- создаёт ссылку в домашней директории на директорию в `/ceph/`,
|
|
|
- ограничивает доступ к домашней папке всем, кроме владельца,
|
|
|
- генерирует пользователям ключи ed25519, рассылает по машинам и регистрирует ключи для беспарольного перемещения пользователя между машинами.
|
|
|
|