Лабораторная работа "Изучение Bash" - efanov/mephi GitHub Wiki
Защита работы проходит в формате беседы с преподавателем. Необходимо продемонстрировать понимание оболочки Bash и умение пользоваться программами, описанными в работе.
- OpenNET — отличный источник информации по Linux на русском языке
- Linux — кратко обо всём
- Шпаргалка начинающего линуксоида
- Искусство командной строки
- The Bash Guide
Если смысл вводимой команды вам непонятен, воспользуйтесь сервисом ExplainShell.
- Работа выполняется: на хостовой или виртуальной машине под управлением 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
.
- Навыки: просмотр содержимого каталогов, перемещение по дереву каталогов.
- Изучаемые команды:
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
, чтобы узнать подробнее о параметрах этой команды.
- Навыки: создание файлов и каталогов, перемещение и переименование объектов файловой системы, копирование файлов и каталогов, просмотр содержимого файлов, удаление файлов.
- Изучаемые команды:
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
из домашнего каталога.
Конвейеры - мощный инструмент объединения команд. В конвейере стандартный вывод команды подаётся на ввод другой. С помощью конвейеров решается множество задач: поиск/замена строк, сортировка, преобразования строк.
Необходимо выполнить следующие задания, объяснив каждый шаг конвейера. Вместо знаков вопроса подставьте нужную команду/аргумент.
- Работа выполняется: на виртуальной машине.
- Навыки: построение конвейеров из команд, сортировка, фильтрация, поиск.
- Изучаемые команды:
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.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.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
, 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
:
- Найдите все файлы и каталоги, имя которых содержит слово
pass
, поиск начните с корневого каталога. - Найдите все файлы и каталоги, имя которых содержит слово
pass
без учёта регистра, поиск начните с корневого каталога. - Найдите все файлы и каталоги, имя которых содержит слово
pass
, ограничив глубину поиска одним каталогом, поиск начните с корневого каталога. - Найдите все файлы и каталоги, имена которых оканчиваются на
.bin
. Поиск необходимо выполнить в каталоге/home
. - Найдите все файлы (и только файлы) с расширением
bak
и удалите их. - Найдите все файлы (и только файлы) с расширениями
txt
иsh
. - Найдите все файлы (и только файлы) в текущем каталоге и выведите только имя файла (без каталога), владельца, группу владельца, количество жёстких ссылок на этот файл и его размер в байтах.
- Найдите все пустые каталоги в текущем каталоге.
- Найдите все пустые каталоги в текущем каталоге и удалите их.
- Найдите и удалите все пустые файлы (и только файлы).
- Найдите все файлы (и только файлы) в текущем каталоге, на которые есть хотя бы одна жёсткая ссылка.
- Найдите файлы и каталоги в каталоге
/etc
, не принадлежащие пользователюroot
. - Найдите все файлы (и только файлы), у которых нет расширения
sh
. - Найдите все файлы (и только файлы), у которых количество жёстких ссылок более двух.
- Найдите все файлы (и только файлы) в каталоге
/usr/bin
, последний доступ к которым осуществлялся более трёх месяцев назад. - Найдите все файлы (и только файлы) в каталогах
/usr/bin
и/usr/share
, созданные или изменённые в течении последних 10 дней. - Найдите и удалите все файлы (и только файлы) в каталоге
/tmp
, которые не менялись более двух недель. - Найдите все файлы (и только файлы) в каталоге
/usr/bin
с установленным флагом suid/sgid.
Используя команды find
и xargs
или параметр -exec
команды find
:
- Найдите все файлы (и только файлы) с расширением
txt
и подсчитайте количество строк во всех этих файлах. - Найдите все каталоги с названием
.svn
и удалите их, включая содержимое этих каталогов, попутно выводя список удалённых файлов на экран. - Найдите все файлы (и только файлы) с расширением
sh
и добавьте им право на исполнение. - Найдите все файлы (и только файлы) с расширением
conf
в каталоге/etc
и подсчитайте их суммарный размер, используя командуdu
.
Протестируйте команды, которые вы написали выше, для файлов и каталогов, в именах которых содержатся пробелы и специальные символы, такие как !
и &
.
Используя команду grep
:
- Из файла
/var/log/messages
вывести строки, содержащие ключевое словоERROR
, без учёта регистра. - Из файла
/var/log/messages
вывести количество строк, не содержащих ключевое словоERROR
, без учёта регистра. - Из файла
/var/log/messages
вывести строки, содержащие только словоERROR
целиком, с учётом регистра. - Вывести количество строк из файла
/etc/group
, совпадающих с шаблономwheel
. - Найти во всех файлах из текущего каталога и вложенных подкаталогов строки, содержащие шаблон
#!/bin/bash
. - Изменить предыдущую команду таким образом, чтобы она выводила дополнительные 10 строк после каждого найденного шаблона.
- Найти во всех файлах с расширением
sh
из текущего каталога и вложенных подкаталогов строки, содержащие словоecho
целиком. В выводе командыgrep
найденные слова выделите цветом. - Измените предыдущую команду таким образом, чтобы команда grep отображала также имя файла и номер строки, в которой было обнаружено совпадение с шаблоном.
В предыдущих задяниях вы познакомились с возможностями командной строки и основными стандартными командами, которых достаточно для повседневного использования в задачах администрирования.
По мере усложнения этих задач, возникает необходимость постоянно вводить множество однотипных команд, возможно, с различными параметрами. Для выполнения сложных задач однотипные команды объединяют в специальный файл — сценарий, который потом можно выполнять как обычную команду. Bash является идеальным языком для автоматизации задач администрирования: выполнение команд, работа с файлами и потоками.
Цель данного задания: научиться разрабатывать собственные сценарии на языке Bash.
Знание языка Bash является залогом успешного решения задач администрирования системы, даже если вы не предполагаете заниматься написанием своих сценариев. Многие системные службы и утилиты используют сценарии Bash, поэтому важно понимать, как они работают и уметь исправлять их, когда это необходимо.
Данная работа является подготовительной для выполнения основного задания курса — разработка собственных интерактивных сценариев командной строки для выполнения задач администрирования средств защиты Linux.
Для редактирования сценариев можно использовать один из следующих Open Source редакторов с подсветкой синтаксиса:
- Vim
- Gedit
- Mousepad
- Geany
- MCEdit
- Visual Studio Code
При выполнении данной работы рекомендуется использовать редактор vim, которым можно пользоваться даже в случаях, когда работа в графической среде невозможна — например, при входе по ssh.
В предыдущей работе для того чтобы вывести текст на экран, использовалась команда echo. В данном примере мы напишем для вывода фразы "Hello world!" специальный сценарий.
- Создайте файл hello.sh.
- Откройте данный файл для редактирования в текстовом редакторе.
- Запишите первую строку файла:
#!/bin/bash
. Это так называемый "шебанг" - специальная инструкция, сообщающая операционной системе, что данный файл нужно воспринимать именно как Bash-сценарий и использовать для его выполнения командую оболочку Bash. - Запишите вторую строку в файл:
echo "Hello world!"
. Данная команда выводит на экран фразу "Hello world!". - Выполните полученный сценарий:
bash hello.sh
- Для того чтобы сценарий можно было выполнить как обычную программу, нужно сделать файл сценария исполняемым:
chmod +x hello.sh
- Попробуйте запустить сценарий как обычную программу:
./hello.sh
. - Попробуйте запустить сценарий без указания пути:
hello.sh
. Данная команда не работает, так как она отсутствует в перечне путей в переменной$PATH
.$PATH
содержит список каталогов, разделённых:
, в которых командная оболочка последовательно ищет исполняемый файл. - Создайте в домашнем каталоге каталог
bin
и переместите туда файлhello.sh
. - Попробуйте запустить сценарий без указания пути:
hello.sh
. Так как сценарий теперь расположен по пути, который есть в$PATH
, команда должна сработать и вывести на экран "Hello world!".
Примечание. Данный сценарий будет доступен только текущему пользователю. Для того чтобы сценарий работал у всех пользователей системы, его нужно скопировать в каталог /usr/local/bin
(для этого потребуются права root
).
Начиная со следующего раздела вам будет необходимо самостоятельно разработать несколько сценариев на языке Bash. Создайте рабочий каталог, в котором будут содержаться ваши сценарии:
mkdir scripts
Каждое задание - отдельный файл со сценарием. Название сценария придумать самостоятельно.
В данном разделе рассмотрим простейшие примеры использования сценариев 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", которая:
- Пожелает пользователю доброго утра.
- Выведет текущее время и календарь на текущий месяц.
- Выведет список дел из файла 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. Разработать сценарий, который выводит файлы из текущего каталога в следующем порядке:
- Каталоги.
- Обычные файлы.
- Символьные ссылки.
- Символьные устройства.
- Блочные устройства.
Формат вывода:
Каталоги:
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
Полезная шпаргалка: 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
В данном разделе необходимо разработать сценарии, которые не только автоматизируют какие-то действия, но и принимают некоторые параметры от пользователя. Таким образом, созданные сценарии можно повторно использовать для различных входных данных.
Аргументы командной строки в сценарий 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 - $(())
.