Лабораторная работа "Изучение Bash" - efanov/mephi GitHub Wiki

Содержание

Порядок защиты работы

Защита работы проходит в формате беседы с преподавателем. Необходимо продемонстрировать понимание оболочки Bash и умение пользоваться программами, описанными в работе.

Общие принципы Shell

Подготовка

Если смысл вводимой команды вам непонятен, воспользуйтесь сервисом ExplainShell.

1. Работа с командной оболочкой и справочной системой

  • Работа выполняется: на хостовой или виртуальной машине под управлением Rocky Linux 9.
  • Навыки: выполнение команд, редактирование команд, просмотр истории, работа с аргументами команд.
  • Изучаемые команды: bash, man, whoami, cal, history, clear, date, echo, sudo, su.

Работа в командной строке происходит в виде интерактивного диалога со специальной программой - командной оболочкой (Bash). В ней выполняются команды, имеющие интерфейс командной строки (далее - просто команды). Команды могут группироваться и объединяться в потоки, а результат их выполнения перенаправляться в файл. Последовательности команд можно объединять в сценарии для повторного использования.

Управление командами осуществляется с помощью аргументов. Аргументы отделяются друг от друга пробелом.

echo  -n   "Hello world!"
\__/   \   \____________/
 \      \   2-й аргумент
  \      1-й аргумент
   команда

Те аргументы, что содержат пробелы, заключаются в кавычки (одинарные ' или двойные "), при этом содержимое внутри кавычек будет является аргументом. Важно помнить и понимать, как командная оболочка обрабатывает команды и аргументы, чтобы научиться правильно их использовать.

Большинство программ в командной строке принимают параметры в стандартном формате:

  • длинный, состоит из двух дефисов -- и английского слова (или нескольких слов, разделённых дефисом), длинные параметры, как правило, имеют "говорящее" название и их проще запомнить, например --list-all, --help, --version;
  • короткий, состоит из дефиса - и алфавитно-цифрового символа ([a-zA-Z0-9]), чаще всего короткие параметры дублируют соответствующие длинные для ускорения набора команд, например -n, -2, -C.

Такие параметры ещё называются позиционно-независимыми, так как для них не имеет значения порядок их следования в командной строке.

Некоторые параметры могут принимать значения:

  • значение для длинного параметра отделяется пробелом или знаком равенства =, например --sort-column=3, --sort-column 3 - эквивалентны;
  • значение для короткого параметра отделяется пробелом или сразу следует за параметром, например -k 10, -k10 - эквивалентны.

Список параметров команды обычно можно узнать, выполнив команду с параметром --help или прочитав справочник с помощью команды man <имя_команды_для_которой_хотим_прочитать_справку>.

Короткие параметры удобны также там, что их можно объединять для ускорения набора команды, например, -abc10 - это то же самое, что и три отдельных параметра: -a -b -c 10.

Пример команды с параметрами:

grep -iw --color -m1 bash /etc/passwd

Экранирование

Специальные символы (! & # $ ' " * ( ) ` и пробел) в командной строке нужно экранировать. Экранировать можно символом обратного слэша \ или заключив весь аргумент в кавычки. Если вы не уверены, что команда будет интерпретирована правильно, всегда заключайте аргументы со спецсимволами в одинарные кавычки.

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

Примеры экранирования:

echo "it's hard to do that" # экранирование одинарной кавычки с помощью двойных
echo 'it'\''s hard to do that' # экранирование одинарной кавычки в одинарных кавычках
echo '"Пример текста в кавычках"' # экранируем двойные кавычки
# строка, которая начинается с символа решётки, является комментарием и игнорируется
echo '# эта строка не является комментарием'

Выполните команды выше и посмотрите на результат.

Рассмотрим пример echo 'it'\''s hard to do that' подробнее:

'it' - первая часть аргумента в одинарных кавычках
\' - одинарная кавычка, экранированная символом обратного слэша (вторая часть аргумента)
's hard to do that' - третья часть аргумента в одинарных кавычках

Таким образом видно, что аргумент может содержать сразу несколько частей, экранированных различными способами. Например: echo 'Это'" всё"' будет один'\ 'аргумент'

Задания

1.1. Откройте эмулятор терминала или выполните вход в систему в режиме командной строки.

1.2. Введите команду whoami и нажмите клавишу Enter. Вы увидите имя пользователя, под которым работаете. Убедитесь, что вы работаете не под пользователем root (системный администратор).

1.3. Нажмите клавишу "стрелка вверх". Терминал перейдёт в режим просмотра истории команд и отобразит последнюю выполненную команду.

1.4. Нажмите клавишу "стрелка вниз", чтобы стереть команду с экрана.

1.5. Выполните команду с опечаткой: whoaim. Что произошло?

1.6. Используя историю команд измените команду whoaim на whoami и снова выполните её. Для этого нажмите стрелку "вверх" дважды, после чего нажмите клавишу Enter.

1.7. Используя команду echo выведите в терминал фразу Hello World:

echo "Hello World"

В данном примере аргумент взят внутрь кавычек, таким образом, фраза "Hello World" станет единым аргументом. Если ввести просто echo Hello World - командная оболочка воспримет это как два самостоятельных аргумента: Hello и World, разделяя их по пробелу.

1.9. Выведите на экран текущие дату и время, выполнив команду date.

1.10. Выведите календарь, выполнив команду cal.

1.11. Прочитайте справку по команде cal и найдите информацию о том, как вывести календарь за три месяца. Для этого выполните команду man cal.

  • Для навигации по странице справки используйте стрелки клавиатуры или клавиши PageUp/PageDown.
  • Для поиска информации в справочной системе нажмите клавишу /, введите поисковый запрос и нажмите Enter. Продолжить поиск с тем же запросом можно последовательным нажатием клавиш /, Enter.
  • После того как вы нашли нужную информацию, завершите чтение справки, нажмите клавишу q. Так можно прочитать справку по любой команде, имеющейся в системе.

Используйте команду man для изучения других команд и выполнения заданий.

1.12. Выведите календарь для трёх месяцев одновременно: текущего, предыдущего и следующего.

1.13. Выведите историю своих команд. Выясните, какая команда за это отвечает.

1.14. Очистите окно терминала, выполнив команду clear. Повторите то же самое, нажав сочетание клавиш Ctrl+L.

2. Перемещение по файловой системе

  • Навыки: просмотр содержимого каталогов, перемещение по дереву каталогов.
  • Изучаемые команды: pwd, cd, ls.

Самостоятельно изучите понятия: дерево файловой системы, файл, каталог, текущий каталог, домашний каталог, корень файловой системы, родительский каталог, подкаталог.

2.1. Установите, в каком каталоге вы сейчас находитесь.

pwd

2.2. Перейдите в корневой каталог (/).

cd /

2.3. Попробуйте перейти на каталог уровнем выше. Проверьте текущий каталог. Изменился ли текущий каталог? Почему?

cd ..
pwd

2.4. Выведите список файлов текущего каталога.

ls

2.5 Выведите список файлов каталога /var.

ls /var

2.6. Вернитесь в домашний каталог.

cd ~

или просто

cd

2.7. Поднимитесь на один каталог вверх. Какой стал текущий каталог? Выведите текущий каталог на экран.

2.8. Выведите список файлов и каталогов из текущего каталога. Что в нём находится?

2.9. Выведите список файлов и каталогов из домашнего каталога так, чтобы можно было определить владельца файлов. Для этого используйте расширенный вывод ls, добавив параметр -l. Используйте справку по команде ls, чтобы узнать подробнее о параметрах этой команды.

3. Работа с файловой системой

  • Навыки: создание файлов и каталогов, перемещение и переименование объектов файловой системы, копирование файлов и каталогов, просмотр содержимого файлов, удаление файлов.
  • Изучаемые команды: touch, mkdir, mv, cp, cat, rm, rmdir, ln.

3.1. Перейдите в домашний каталог.

3.2. Создайте в домашнем каталоге подкаталог fruits.

mkdir fruits

3.3. Перейдите в каталог /. Находясь в каталоге /, создайте в своём домашнем каталоге подкаталог animals.

mkdir ~/animals

3.4. Создайте во временном каталоге (/tmp) пустой файл temp.

touch /tmp/temp

3.5. Перейдите в ранее созданный каталог fruits.

3.6. В текущем каталоге (fruits) создайте пустые файлы apple, banana, pineaple, lion.

3.7. Находясь в каталоге fruits, в ранее созданном каталоге animals создайте пустые файлы cat.txt, dog.txt, elephant.txt.

3.8. Определите дату последнего изменения файла apple.

3.9. Выведите из текущего каталога (fruits) файлы, имя которых начинается на b.

ls b*

3.10. Выведите из текущего каталога файлы, имя которых оканчивается на a.

3.11. Выведите из текущего каталога файлы, имя которых содержит буквы b или i:

ls [bi]*

или

ls b* i*

Так как шаблоны раскрываются командной оболочкой до выполнения команды, вторая команда завершится с ошибкой.

3.12. Скопируйте файл /etc/passwd в домашний каталог:

cp /etc/passwd ~

3.13. Выведите содержимое файла /etc/issue на экран с помощью команды cat.

3.14. Скопируйте файл /etc/issue на файл apple так, чтобы заменить файл apple.

3.15. Выведите содержимое файла apple на экран. Оно должно совпадать с содержимым файла /etc/issue.

3.16. Переместите файл lion в каталог animals.

3.17. В имени файла pineaple есть опечатка. Переименуйте файл pineaple в pineapple.

3.18. Изучите команду wc. Подсчитайте с её помощью количество строк в файле /etc/passwd.

3.19. Перейдите в домашний каталог и создайте в нём символьную ссылку с именем passwd_link, указывающую на /etc/passwd.

ln -s /etc/passwd passwd_link

3.20. Создайте в домашнем каталоге жёсткую ссылку с именем history_hard, указывающую на ~/.bash_history.

ln .bash_history history_hard

Обратите внимание, что жёсткие ссылки создаются командой ln по умолчанию.

3.21. Отследите с помощью команды ls -l изменение количества жёстких ссылок у файла ~/.bash_history и сравните его с другими файлами домашнего каталога.

3.22. Удалите файл fruits/apple.

3.23. Создайте пустой каталог rmme, затем удалите его командой rmdir.

3.24. Попробуйте удалить каталог fruits:

rmdir fruits

Возникнет ошибка. Почему?

3.25. Удалите каталог fruits командой rm -fr.

rm -fr fruits

Примечание: rm -fr удаляет все каталоги с подкаталогами, не спрашивая и не помещая их в корзину. Этой командой надо пользоваться очень внимательно и только в крайних случаях, так как можно случайно удалить важные данные навсегда.

3.26. Выведите на экран содержимое файлов /etc/passwd и /var/log/boot.log одновременно, используя команду cat.

3.27. Скопируйте каталог /etc/systemd/ в домашний каталог:

cp /etc/systemd/ ~

Данная команда не выполнится, так как cp по умолчанию копирует файлы по одному. Чтобы скопировать каталог целиком, включая все подкаталоги, используйте команду:

cp -r /etc/systemd/ ~

3.28. Удалите каталог systemd из домашнего каталога.

4. Конвейеры. Обработка текстовых файлов

Конвейеры - мощный инструмент объединения команд. В конвейере стандартный вывод команды подаётся на ввод другой. С помощью конвейеров решается множество задач: поиск/замена строк, сортировка, преобразования строк.

Необходимо выполнить следующие задания, объяснив каждый шаг конвейера. Вместо знаков вопроса подставьте нужную команду/аргумент.

  • Работа выполняется: на виртуальной машине.
  • Навыки: построение конвейеров из команд, сортировка, фильтрация, поиск.
  • Изучаемые команды: cut, grep, sort, wc, tr, uniq, head, tail, fold, column, less.

Примечание. Вместо ??? необходимо подставить нужную команду, чтобы решить задачу. Используйте команду man, чтобы получить больше информации об используемых командах и их аргументах.

4.1. Вывести на экран всех пользователей системы с сортировкой по алфавиту:

cut -d: -f1 /etc/passwd | ???

Команда cut осуществляет разбиение каждой строки файла /etc/passwd на столбцы, используя двоеточие в качестве разделителя и выводит первый столбец.

Примечание. Предварительно изучите содержимое файла /etc/passwd и вывод команды cut.

Подсказка: файлы вида /etc/passwd, как и вывод многих команд не очень удобно читать по двум причинам:

  • они разделены по двоеточию, а колонки визуально не выровнены в виде таблицы, и зачастую сложно найти нужную колонку в файле;
  • они могут быть слишком длинными и не помещаться на экран.

Для того чтобы облегчить изучение вывода команд, используйте less и column. less позволяет читать длинный вывод команды, а column форматирует данные в виде таблицы, которые удобнее воспринимать. Разберите следующий пример, изучив man по каждой из команд конвейера:

cut -d: -f1,7 /etc/passwd | column -ts: | less

4.2. Изменить предыдущую команду таким образом, чтобы помимо имени пользователя на экране также выводился его идентификатор. Подсказка: посмотрите содержимое файла /etc/passwd, чтобы найти, в каком из полей, разделенных двоеточием, находится UID.

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

grep :/bin/bash /etc/passwd | wc -l

Команда grep используется для поиска строк в файлах, в данном случае искомая строка - :/bin/bash. Подробнее смотрите в man grep.

4.4. Вывести на экран только имена пользователей, использующих командную оболочку /bin/bash, отсортировав их по алфавиту в обратном порядке:

grep :/bin/bash /etc/passwd | ??? | ???

Подсказка: внимательно изучите предыдущие примеры.

4.5. Выполнить сортировку установленных системных пакетов по размеру (большего к меньшему) и вывести первые 25 из них:

rpm -qa --qf '%{NAME}: %{SIZE}\n' | sort -rn -k2,2 | ???

4.6. Вывести все файлы и каталоги из пакета man, подсчитать для каждого из них размер, игнорируя ошибки, и выполнить сортировку по размеру:

rpm -ql man-db | xargs -l du    2> /dev/null  | sort -n
#               ^ для каждого   ^ игнорировать  ^ сортировать числа
#                файла вызвать      ошибки
#                 команду du

4.7. При помощи программы top вывести список работающих процессов и отсортировать их по имени процесса (аргумент команды sort заполнить самостоятельно):

top -b -n1 | sort ???

4.8. Изменить вывод команды top таким образом, чтобы в первой колонке отображалось имя процесса, во второй - объём занимаемой памяти:

top -b -n1 | tail -n+8 | cut -c7-  | tr -s ' ' '\t' | cut -f11,5
#            ^ убирает   ^ отделяет  ^ "схлопывает"   ^ в 11 колонке - имя процесса,
#             заголовки      PID         пробелы        в 5 колонке - объём памяти

4.9. Вывести файл /etc/passwd в отсортированном по пользователям виде, заменив знаки двоеточия пробелами:

??? | tr ":" " "

4.10. Вывести файл /etc/passwd в отсортированном по пользователям виде в две колонки - в первой - имя пользователя, во второй - командная оболочка пользователя:

???

Подсказка: используйте команды из предыдущих двух примеров. 4.11. На примере файла user-manual.txt из документации git исследовать конвейеры. Примечание. Установите пакет git с помощью команды dnf install -y git, если он отсутствует в системе.

cd /usr/share/doc/git*

4.12. Выполнить анализ частоты появления каждого слова в файле user-manual.txt. Конвейер нужно строить по шагам, наблюдая за изменением результата. Определить этап получения первичной информации, этап обработки и этап представления результатов.

cat user-manual.txt | tr ' ' '\012' | tr '[:upper:]' '[:lower:]' | tr -d '[:punct:]' | grep -v '[^a-z]' | sort | uniq -c | sort -rn | head -5

Или:

cat user-manual.txt | tr -cs '[:alpha:]' '\n' | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort -rn | head -5

Почему различаются результаты работы конвейеров?

4.13. Выполнить анализ частоты появления каждого символа.

tr '[:lower:]' '[:upper:]' < user-manual.txt |  tr -d '[:punct:] \n\t' | fold -w1 | sort | uniq -c | sort -rn | head -5

4.14. Вывести самые часто встречаемые слова, в которых количество символов больше семи:

tr -cs '[:alpha:]' '\n' < user-manual.txt | less | grep -E ........ | sort | uniq -c | sort -rn | head

5. Работа с сетью

Несколько полезных команд помогут при работе в Интернете из командной строки.

5.1. Проверьте доступность сети командой ping.

ping ya.ru

Подсказка. Команда ping работает "вечно". Чтобы прервать её работу, нажмите Ctrl+C.

5.2. Выясните прогноз погоды.

curl wttr.in

Подсказка. С помощью curl можно загрузить любой адрес сети.

5.3. Узнайте свой IP-адрес.

IP-адрес может быть внутренним (адрес в локальной сети) и внешним (тот, через который осуществляется доступ в интернет).

Внутренний адрес:

hostname -I

Внешний адрес:

curl ifconfig.me

5.4. С помощью команды wget можно загрузить любой файл из Интернета и сохранить его. Загрузите файл https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README-ru.md и прочитайте его командой less.

6. Получение прав root

Получить права администратора можно тремя способами.

6.1. Самый простой способ - выйти из системы и войти от имени пользователя root. Выполните команды id, whoami и убедитесь что это так.

Примечание. Для выполнения следующих заданий не забудьте переключиться в пользователя student.

6.2. Пользователь, который входит в группу wheel, может выполнять команды от имени root через команду sudo. При выполнении команды sudo нужно вводить пароль пользователя (только первый раз, последующие выполнения команды sudo не будут спрашивать пароль).

sudo id
id # сравните вызовы команд
# следующие вызовы sudo не запрашивают пароль
sudo less /var/log/messages

Попробуйте прочитать /var/log/messages без sudo.

6.3. Если пользователь не входит в группу wheel, можно переключиться в root с помощью команды su, зная пароль root. Особенность команды su - она, в отличие от sudo, запускает полноценный сеанс пользователя root, не забудьте выйти из него командой exit.

su
# нужно знать пароль root!
id
whoami
exit # не забываем завершить работу под root, в противном случае можно повредить систему!

Изучение команды find

Изучите команды find, xargs, и grep с помощью команды man.

Создайте тестовый каталог. Свои команды проверять нужно находясь в каталоге test_finging

mkdir test_finding
cd test_finding
mkdir -p './-test dir/empty dir'
touch './-test dir'/{1..10}.sh
touch './-test dir'/{1..10}.txt
touch './-test dir'/{1..10}.bak
echo 'print echoing' > './-test dir'/1.sh
echo 'echo print' > './-test dir'/2.sh
echo 'too many errors' > './-test dir'/3.txt
echo 'are you looking an error here?' > './-test dir'/4.txt
echo 'There was a BIG ERROR.' > './-test dir'/5.txt

Задания

Используя команду find:

  1. Найдите все файлы и каталоги, имя которых содержит слово pass, поиск начните с корневого каталога.
  2. Найдите все файлы и каталоги, имя которых содержит слово pass без учёта регистра, поиск начните с корневого каталога.
  3. Найдите все файлы и каталоги, имя которых содержит слово pass, ограничив глубину поиска одним каталогом, поиск начните с корневого каталога.
  4. Найдите все файлы и каталоги, имена которых оканчиваются на .bin. Поиск необходимо выполнить в каталоге /home.
  5. Найдите все файлы (и только файлы) с расширением bak и удалите их.
  6. Найдите все файлы (и только файлы) с расширениями txt и sh.
  7. Найдите все файлы (и только файлы) в текущем каталоге и выведите только имя файла (без каталога), владельца, группу владельца, количество жёстких ссылок на этот файл и его размер в байтах.
  8. Найдите все пустые каталоги в текущем каталоге.
  9. Найдите все пустые каталоги в текущем каталоге и удалите их.
  10. Найдите и удалите все пустые файлы (и только файлы).
  11. Найдите все файлы (и только файлы) в текущем каталоге, на которые есть хотя бы одна жёсткая ссылка.
  12. Найдите файлы и каталоги в каталоге /etc, не принадлежащие пользователю root.
  13. Найдите все файлы (и только файлы), у которых нет расширения sh.
  14. Найдите все файлы (и только файлы), у которых количество жёстких ссылок более двух.
  15. Найдите все файлы (и только файлы) в каталоге /usr/bin, последний доступ к которым осуществлялся более трёх месяцев назад.
  16. Найдите все файлы (и только файлы) в каталогах /usr/bin и /usr/share, созданные или изменённые в течении последних 10 дней.
  17. Найдите и удалите все файлы (и только файлы) в каталоге /tmp, которые не менялись более двух недель.
  18. Найдите все файлы (и только файлы) в каталоге /usr/bin с установленным флагом suid/sgid.

Используя команды find и xargs или параметр -exec команды find:

  1. Найдите все файлы (и только файлы) с расширением txt и подсчитайте количество строк во всех этих файлах.
  2. Найдите все каталоги с названием .svn и удалите их, включая содержимое этих каталогов, попутно выводя список удалённых файлов на экран.
  3. Найдите все файлы (и только файлы) с расширением sh и добавьте им право на исполнение.
  4. Найдите все файлы (и только файлы) с расширением conf в каталоге /etc и подсчитайте их суммарный размер, используя команду du.

Протестируйте команды, которые вы написали выше, для файлов и каталогов, в именах которых содержатся пробелы и специальные символы, такие как ! и &.

Используя команду grep:

  1. Из файла /var/log/messages вывести строки, содержащие ключевое слово ERROR, без учёта регистра.
  2. Из файла /var/log/messages вывести количество строк, не содержащих ключевое слово ERROR, без учёта регистра.
  3. Из файла /var/log/messages вывести строки, содержащие только слово ERROR целиком, с учётом регистра.
  4. Вывести количество строк из файла /etc/group, совпадающих с шаблоном wheel.
  5. Найти во всех файлах из текущего каталога и вложенных подкаталогов строки, содержащие шаблон #!/bin/bash.
  6. Изменить предыдущую команду таким образом, чтобы она выводила дополнительные 10 строк после каждого найденного шаблона.
  7. Найти во всех файлах с расширением sh из текущего каталога и вложенных подкаталогов строки, содержащие слово echo целиком. В выводе команды grep найденные слова выделите цветом.
  8. Измените предыдущую команду таким образом, чтобы команда grep отображала также имя файла и номер строки, в которой было обнаружено совпадение с шаблоном.

Изучение сценариев Bash

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

По мере усложнения этих задач, возникает необходимость постоянно вводить множество однотипных команд, возможно, с различными параметрами. Для выполнения сложных задач однотипные команды объединяют в специальный файл — сценарий, который потом можно выполнять как обычную команду. Bash является идеальным языком для автоматизации задач администрирования: выполнение команд, работа с файлами и потоками.

Цель данного задания: научиться разрабатывать собственные сценарии на языке Bash.

Знание языка Bash является залогом успешного решения задач администрирования системы, даже если вы не предполагаете заниматься написанием своих сценариев. Многие системные службы и утилиты используют сценарии Bash, поэтому важно понимать, как они работают и уметь исправлять их, когда это необходимо.

Данная работа является подготовительной для выполнения основного задания курса — разработка собственных интерактивных сценариев командной строки для выполнения задач администрирования средств защиты Linux.

Для редактирования сценариев можно использовать один из следующих Open Source редакторов с подсветкой синтаксиса:

  • Vim
  • Gedit
  • Mousepad
  • Geany
  • MCEdit
  • Visual Studio Code

При выполнении данной работы рекомендуется использовать редактор vim, которым можно пользоваться даже в случаях, когда работа в графической среде невозможна — например, при входе по ssh.

Материалы для подготовки

Ваш первый сценарий

В предыдущей работе для того чтобы вывести текст на экран, использовалась команда echo. В данном примере мы напишем для вывода фразы "Hello world!" специальный сценарий.

  1. Создайте файл hello.sh.
  2. Откройте данный файл для редактирования в текстовом редакторе.
  3. Запишите первую строку файла: #!/bin/bash. Это так называемый "шебанг" - специальная инструкция, сообщающая операционной системе, что данный файл нужно воспринимать именно как Bash-сценарий и использовать для его выполнения командую оболочку Bash.
  4. Запишите вторую строку в файл: echo "Hello world!". Данная команда выводит на экран фразу "Hello world!".
  5. Выполните полученный сценарий: bash hello.sh
  6. Для того чтобы сценарий можно было выполнить как обычную программу, нужно сделать файл сценария исполняемым: chmod +x hello.sh
  7. Попробуйте запустить сценарий как обычную программу: ./hello.sh.
  8. Попробуйте запустить сценарий без указания пути: hello.sh. Данная команда не работает, так как она отсутствует в перечне путей в переменной $PATH. $PATH содержит список каталогов, разделённых :, в которых командная оболочка последовательно ищет исполняемый файл.
  9. Создайте в домашнем каталоге каталог bin и переместите туда файл hello.sh.
  10. Попробуйте запустить сценарий без указания пути: hello.sh. Так как сценарий теперь расположен по пути, который есть в $PATH, команда должна сработать и вывести на экран "Hello world!".

Примечание. Данный сценарий будет доступен только текущему пользователю. Для того чтобы сценарий работал у всех пользователей системы, его нужно скопировать в каталог /usr/local/bin (для этого потребуются права root).

Порядок выполнения работы

Начиная со следующего раздела вам будет необходимо самостоятельно разработать несколько сценариев на языке Bash. Создайте рабочий каталог, в котором будут содержаться ваши сценарии:

mkdir scripts

Каждое задание - отдельный файл со сценарием. Название сценария придумать самостоятельно.

1. Автоматизация выполнения команд

В данном разделе рассмотрим простейшие примеры использования сценариев Bash - объединение нескольких однотипных команд. Это простейшие сценарии, которые не принимают аргументов, не содержат сложных условий, циклов и т.п.

Например, для создания каталога, доступного всем пользователям на чтение и запись, необходимо выполнить два действия:

  • создать каталог;
  • изменить права доступа для каталога.

1.1. Разработать сценарий, который создаёт каталог /tmp/shared и устанавливает на него права доступа rwxrwxrwx.

Пример решения данной задачи на языке сценариев Bash:

#!/bin/bash
DIR=/tmp/shared
mkdir -p "$DIR"
chmod 777 "$DIR"

1.2. Вывести количество файлов в домашнем каталоге, которые заканчиваются на .txt. Создайте несколько таких файлов для тестирования.

1.3. Вывести текущие переменные окружения в отсортированном по алфавиту порядке.

1.4. Разработать программу "Good morning", которая:

  1. Пожелает пользователю доброго утра.
  2. Выведет текущее время и календарь на текущий месяц.
  3. Выведет список дел из файла TODO домашнего каталога пользователя.

1.5. Найти и вывести пути до файлов из каталога /usr (включая подкаталоги), размер которых больше 20 Мб. Подсказка: man find.

1.6. Подсчитать количество файлов, количество скрытых файлов в домашнем каталоге текущего пользователя и вывести результат в формате:

Домашний каталог пользователя
<User>
содержит обычных файлов:
XX
скрытых файлов:
YY

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

1.8. Вывести количество процессов, запущенных от имени текущего и от имени пользователя root в формате:

Процессов пользователя:
<User>
XX
Процессов пользователя root:
YY

1.9. Найти и вывести 5 процессов, потребляющих больше всего памяти в системе. Подсказка: man ps

1.10. Вывести файлы и каталоги из домашнего каталога пользователя, упорядочив их по размеру. Подсказка: использовать команды du и sort.

1.11. Разработать сценарий, который выводит файлы из текущего каталога в следующем порядке:

  1. Каталоги.
  2. Обычные файлы.
  3. Символьные ссылки.
  4. Символьные устройства.
  5. Блочные устройства.

Формат вывода:

Каталоги:
drwxr-xr-x  2 root root           560 сен 13 01:34 block/
drwxr-xr-x  2 root root           120 сен 13 01:34 bsg/
drwxr-xr-x  3 root root            60 июн 19 06:41 bus/
drwxr-xr-x  2 root root          3680 сен 13 01:34 char
...
Обычные файлы:
...
Символьные ссылки:
...
Символьные устройства:
...
Блочные устройства:
...

Когда сценарий будет готов, скопируйте его в каталог ~/bin для тестирования. Далее протестируйте сценарий для каталогов /, /dev, /tmp.

Подсказка: ls -l /dev | grep ^b

2. Перенаправление стандартного ввода/вывода

Полезная шпаргалка: Bash Redirections Cheat Sheet.

Краткий справочник:

  • > - записать стандартного вывод (stdout) в файл (содержимое файла будет безвозвратно утеряно)
  • 1> - полностью аналогично предыдущему
  • 2> - аналогично, но для вывода ошибок (stderr)
  • &> - аналогично, но для двух стандартных выводов сразу - stderr и stdout
  • >> - дописать стандартный вывод (stdout) в файл (содержимое файла будет сохранено)
  • 2>> - аналогично, но для stderr
  • &>> - аналогично, но для двух стандартных выводов сразу - stderr и stdout
  • < file - перенаправить файл в стандартный ввод (stdin)
  • > &2 - всё, что программа выводит в stdout, будет перенаправлено в stderr
  • ls | wc - перенаправляет stdout ls в stdin wc
  • ls |& wc - перенаправляет stderr и stdout ls в stdin wc
  • /dev/null - универсальный файл-"чёрная дыра", в неё можно отправить любой ненужный вывод
  • : - универсальная команда, которая не выводит ничего и всегда возвращает истинный код возврата, в неё можно отправить любой ненужный вывод

Обычно перенаправление вывода размещают после команды - наиболее логичным способом:

echo error >&2
ls > /tmp/list

Но возможен вариант размещения перед командой, что удобно в случае, когда нужно сосредоточиться на аргументах команды:

>&2 echo error
> /tmp/list ls -l
< /tmp/list grep 1

Основная сложность проявляется в том, что и stderr и stdout по умолчанию выводятся на экран и никак не отличаются друг от друга. Проверить stderr это или stdout можно перенаправлением в /dev/null.

Например:

ls /non-existent
ls: невозможно получить доступ к /non-existent: Нет такого файла или каталога
# попробуем спрятать ошибку. Так не работает, так как ошибка выводится в stderr
ls /non-existent > /dev/null
# теперь пробуем перенаправить поток ошибок:
ls /non-existent 2> /dev/null
# получилось! Теперь на экран ничего не выводится

# можно заблокировать любой вывод программы
# это нужно, когда мы просто хотим узнать код возврата
ls /non-existent &> /dev/null
# данный способ годится только для Bash
# в более старых системах приходилось делать так:
ls /non-existent 2> /dev/null >&2

Частая ошибка - попытка найти что-то в выводе программы с помощью grep, но вывод осуществляется "мимо" stdout, в итоге мы получим весь вывод просто на экран.

# вы можете использовать grep, чтобы найти нужный параметр в справке
ls --help | grep ссыл
                             следовать по символьным ссылкам в командной
                             следовать по всем символьным ссылкам в командной
  -L, --dereference          показывая информацию для символьной ссылки,
                             показывать информацию о файле, на который ссылка
                             ссылается
# но с ошибками не так просто! Эта команда никак не поможет с поиском:
ls /{1..100} | grep 99
# чтобы найти ошибку в гуще других, нужно чуть изменить команду
ls /{1..100} |& grep 99

Удобство перенаправления потоков в Bash (по сравнению с такими языками как C) компенсируется тем, что символы >, <, |, которые нередко встречаются в повседневной работе, нужно экранировать.

Некоторые команды меняют свой вывод в зависимости от того, перенаправлен ли их вывод или нет:

ls /sbin
# NetworkManager         fsck.ext4                    lvmsadc                                  setenforce
# accessdb               fsck.minix                   lvmsar                                   setfiles
# addgnupghome           fsck.xfs                     lvreduce                                 setsebool
# addpart                fsfreeze                     lvremove                                 sfdisk
# ...

ls /sbin | head
# NetworkManager
# accessdb
# addgnupghome
# addpart
# adduser
# agetty
# alternatives
# anacron
# applygnupgdefaults
# arpd

Это происходит по причине, что ls определяет, что stdout в данный момент не связан с терминалом и меняет формат вывода по умолчанию. Некоторые команды меняют вывод для удобства пользователя - список файлов превращается в колонки и раскрашивается цветами, а когда вы перенаправляете вывод команды, она понимает, что вывод будет обработан и выводит необработанный список.

Вы можете создавать сценарии, которые также принимают на стандартный ввод (stdin) некий ввод и обрабатывает его с помощью стандартных команд. Для этого нужно просто не указывать файл для обрабатывающей команды. Например, нужно разработать сценарий (mygrep.sh), который принимает на стандартный ввод текст и выводит строки, совпадающие с заранее запрограммированным шаблоном.

#!/bin/bash
grep -i bash

Данный сценарий будет фактически упрощённым аналогом команды grep, где все необходимые аргументы уже заданы. Его можно использовать следующим образом:

cat /etc/passwd | ./mygrep.sh
< /etc/passwd ./mygrep.sh

Для тестирования следующих заданий выполните подготовительные действия:

  • создайте в домашнем каталоге файл numbers.txt, в который запишите 10 000 натуральных чисел (см. команду seq);
  • создайте в домашнем каталоге файл users.txt, в который запишите имена всех пользователей системы (используйте cut и /etc/passwd);
  • создайте в домашнем каталоге файл bash.txt, в который запишите содержимое двоичного файла /bin/bash в текстовом виде (используйте od);
  • создайте в домашнем каталоге файл services.txt, который будет идентичным файлу /etc/services (скопировать файл с новым именем).

2.1. Разработать сценарий, который ведёт в файле /tmp/run.log последовательный журнал запусков:

  • в конец журнала добавляет строку с датой и временем запуска сценария (используйте команду date для фиксации даты и времени запуска сценария);
  • в стандартный вывод (stdout) - выводит фразу "Hello, World!"
  • в стандартный вывод ошибок (stderr) - выводит количество предыдущих запусков программы (для этого достаточно подсчитать количество строк в журнале).

Убедиться в правильности работы программы и выводе различных сообщений в различные потоки вывода:

2.1.sh > /dev/null # должен вывести счётчик запусков, счётчик должен увеличиваться
2.1.sh 2> /dev/null # должен вывести Hello, World!

2.2. Разработать сценарий, который запускает сценарий, разработанный в предыдущем задании, а также открывает журнал запусков предыдущего сценария в программе less.

2.3. Разработать сценарий, который для bash.txt (созданного ранее):

  • сохранит строки, которые содержат сочетание символов 000000 в файл /tmp/zeros;
  • сохранит строки, которые не содержат сочетания символов 000000 - в файл /tmp/nozeros;
  • выведет 10 первых и 10 последних строк от каждого из файлов /tmp/zeros и /tmp/nozeros.

2.4. Разработать сценарий, который считывает построчно стандартный ввод и выводит только те строки, которые содержат слово bin целиком в стандартный вывод ошибок. Для проверки сценария используйте конвейер с командой 'ls /.

2.5. Разработать сценарий, который для всех файлов с расширением txt в домашнем каталоге пользователя:

  • выведет список таких файлов;
  • выведет суммарный размер в байтах и строках для файлов с расширением txt.

Подсказка. Для решении этой задачи создайте временный файл в каталоге /tmp, по окончании работы сценария удалите его.

2.6. Разработать сценарий поиска дубликатов файлов (файлы, с одинаковым содержимым), который выводит сначала количество дубликатов, затем имя файла. Файлы без дубликатов выводиться не должны.

Для подготовки выполните команды:

mkdir dups
cd dups
touch {1..3}.txt
echo 4 > 4.txt
echo 4 > 5.txt
echo 6 > 6.txt

Подсказка. Наиболее эффективным методом поиска дубликатов является вычисление их хэш-суммы с последующей их обработкой. У файлов-дубликатов хэш-суммы будут одинаковыми. Используйте команды sort, uniq, grep, tr, cut. Изучите как команды sort и uniq работают с колонками.

md5sum *.txt | ???? -k? | ???? -? -? ?? | ... | ... | ...

Должен получиться вывод:

2	4.txt
3	1.txt

3. Аргументы командной строки и переменные

В данном разделе необходимо разработать сценарии, которые не только автоматизируют какие-то действия, но и принимают некоторые параметры от пользователя. Таким образом, созданные сценарии можно повторно использовать для различных входных данных.

Аргументы командной строки в сценарий Bash передаются также как и в обычную программу вроде grep или wc. Внутри сценария используются специальные переменные $1, $2 и т.п. - по порядку аргументов.

$ ./myscript.sh foo bar 1 2 3
Первые два аргумента: foo bar
Сумма третьего, четвёртого и пятого аргументов: 6
$ cat myscript.sh
#!/bin/bash
echo "Первые два аргумента: $1 $2"
echo "Сумма третьего, четвёртого и пятого аргументов: $(($3+$4+$5))"

Исследуйте, как ведут себя специальные переменные Bash самостоятельно:

$1, $2 - первый аргумент, второй аргумент и т.п.; $# - количество аргументов командной строки, переданные сценарию; $* - все аргументы, переданные сценарию, объединённые в один; $@ - все аргументы, переданные сценарию, по отдельности.

При обработке аргументов всегда используйте экранирующие кавычки. Это обезопасит код от странных ошибок, связанные с тем, что во входных данных могут содержаться пробелы и специальные символы:

# НЕПРАВИЛЬНО!
echo $1 $2 $3
grep foo $1
echo $*
du $@ 
# Правильно:
echo "$1 $2 $3"
grep foo -- "$1"
echo "$*"
du -- "$@"
# исключение составляет переменная $#, это всегда число:
grep -m$# /etc/passwd -- "$1"
# при передаче аргумента (напр., имени файла) в другую команду, всегда отделяйте
# пользовательские данные от аргументов команды символами --. Это гарантирует,
# что ваша команда будет работать даже для файлов, которые начинаются с символа -.
grep "$1" /etc/passwd # неправильно, будет ошибка, если $1 == --help
grep /etc/passwd -- "$1" # правильно

Переменные в Bash аналогичны аргументам. Однако в отличие от аргументов, переменную, прежде чем использовать, необходимо объявить:

NAME=Вася
# если в данных содержится пробел, их надо экранировать
FIO="Иванов Иван Иванович"
# в переменную удобно записывать вывод другой команды:
USERS=$(grep /bin/bash /etc/passwd | cut -d: -f1)
# при копировании одной переменной в другую экранирование не требуется:
FIO_COPY=$FIO # экранирование не нужно
# аналогично, не нужно экранирование при записи в переменную 
# результата работы какой-либо команды
FILES=$(ls) # экранирование не нужно
# однако при объединении нескольких команд без экранирования не обойтись:
FIO_AND_ID="$FIO $(id)" # требуется экранировать, так как объединяем результат

Некоторые полезные переменные:

$USER # имя текущего пользователя
$HOST # имя узла
$HOME # путь до домашнего каталога текущего пользователя
$RANDOM # случайное число
$LANG # текущие настройки локализации (язык и кодировка)
$$ # PID текущего процесса (сценария)
$? # код возврата предыдущей команды

3.1. Разработать сценарий, который выводит на экран количество переданных ему аргументов. Скопировать его в $HOME/bin для дальнейшего использования его в других сценариях.

3.2. Разработать сценарий, который вызывает предыдущий сценарий дважды: первый раз с объединённым полным списком аргументов, второй раз - со списком всех переданных ему аргументов по отдельности.

3.3. Разработать сценарий-тестировщик, который будет тестировать ваши сценарии на различные входные данные. Первым аргументом данный сценарий должен принимать имя файла сценария, который необходимо протестировать. Запрограммируйте следующие сценарии тестирования:

  • с аргументами "1" "2" "3";
  • с пятью случайными числами (см. переменную $RANDOM);
  • с аргументами "foo" "bar" "foobar" "foo bar";
  • с аргументами "foo" "--foo" "--help" "-l".

Протестируйте с помощью разработанного сценария-тестировщика два предыдущих сценария.

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

3.5. Разработать сценарий, который вызывает команду grep и принимает следующие аргументы:

  • текст, который нужно найти;
  • файл, в котором нужно найти этот текст;
  • максимальное количество строк, которое нужно вывести на экран.

Вывод команды grep отсортировать и пронумеровать.

3.6. Разработать сценарий, который выводит в одну строку имя пользователя, его домашний каталог, а также количество символов в этих двух переменных. Например: root /root 9. Подсказка: изучите аргументы команды echo, wc, математические вычисления в Bash - $(()).

⚠️ **GitHub.com Fallback** ⚠️