Снова Баш.
На прошлой неделе мы взаимодействовали с Башем как с REPL'ом, то есть вводили команду, нажимали Enter и получали результат. На этой неделе мы будем работать с Башем, как с языком программирования и научимся писать скрипты (сценарии), чтобы автоматизировать что-либо, а также изучим основные конструкции в программировании, такие как условия, циклы, переменные.
Скрипт (сценарий) — в простейшем виде это файл, в котором содержатся команды, которые ты построчно выполняешь в командной строке. Командная оболочка читает этот файл и выполняет команды друг за другом, как если бы они вводились вручную в командной строке.
Сначала пару абзацев про полезные штуки, которые называются alias'ы и поиск по истории команд.
Ты уже знаешь, что стрелочками вверх-вниз можно возвращать команды, которые были введены ранее и таким образом не вводить их снова. Но есть ещё более быстрые способы запуска частых команд.
Один из них — создание алиасов. Ты просто даешь длинной команде короткое название и пользуешься им. Например, чтобы подключаться к серверу, мы вводили ssh username@hostname
, довольно много символов.
Попробуй выполнить следующую команду(только замени username на свой логин):
alias s="ssh username@hostname"
Теперь просто выполни команду s
. Эта команда выполнит подключение. Удобно, не правда ли? Таким образом, можно назначить алиасы на все часто используемые команды. Например, git push
→ gp
и экономить нажатия.
Правда тут есть один нюанс. Назначенные таким образом алиасы пропадут после перезапуска сессии. Чтобы этого не произошло, нужно их сохранить в файл ~/.bash_profile и применить изменения:
echo 'alias gp="git push"' >> ~/.bash_profile
. ~/.bash_profile
Чтобы посмотреть список всех алиасов, набери команду alias.
Вторая полезная штука — это поиск по истории команд. Нажми Ctrl+R и начни вводить команду. Тут я думаю и так всё понятно.
Ввод-вывод
Окей, теперь напишем первый скрипт. Подключись к удаленному серверу и выполняй примеры на нём.
Команды вводятся построчно, новая строка — новая команда. Можно вводить в одну строку, если разделять точкой с запятой, но так делать не надо.
Выводить информацию на экран можно с помощью команды echo
:
Создай файл hello со следующим содержимым:
#!/bin/bash
#
# Скрипт поприветствует целый мир три раза
echo 'Hello World'
echo 'Hello World'
echo 'Hello World'
Сохрани его. Давай посмотрим, что тут у нас.
Первая строка начинается с конструкции шебанг (#!
), шебанг сообщает системе, через что запустить скрипт, в данном случае мы указали путь до Баша, поэтому скрипт будет вызван в Баше.
Дальше, на второй и третьей строке находится символ решетки (#
). Это комментарии. Всё, что дальше на строке после символа решетки, это просто текст, который никак не выполняется. Комментарии используются для пояснений и когда нужно временно отключить какой-то участок кода, чтобы он не выполнялся.
Давай теперь запустим этот скрипт. Нужно дать ему права на выполнение: chmod +x hello
и выполнить: ./hello
А теперь шок-контент! Сейчас ты узнаешь, как создавать свои собственные команды, такие как cat
, pwd
или ls
.
Все эти команды хранятся в директориях, перечисленных в переменной окружения $PATH
, можешь заглянуть внутрь и убедиться: echo $PATH
. Создай собственную директорию bin
в своей домашней директории: mkdir ~/bin
, затем открой уже знакомый файл .bash_profile
и добавь в него строку export PATH="${PATH}:~/bin"
. Файл .bash_profile
автоматически исполняется каждый раз, когда ты подключаешься к серверу, и эта команда будет добавлять к глобальному $PATH твою личную директорию bin, таким образом мы сможешь вызывать эти скрипты откуда угодно.
Переподключить к серверу. Посмотри ещё раз в $PATH
, теперь там появилось ~/bin
. Что это значит? А то, что теперь ты можешь поместить свой скрипт в эту директорию и запускать его откуда угодно как команду!
Перенеси туда hello
и попробуй вызвать его как команду hello
, при этом находясь где-то за пределами директории.
Давай теперь усовершенствуем скрипт и научим его приветствовать по имени, которое будет передаваться ему на вход.
Переменные
Убедись, что ты на сервере и открой в Nano файл ~/bin/hello
:
#!/bin/bash
my_var="yes"
echo "Первый параметр: $1"
echo "Второй параметр: $2"
echo "Третий параметр: $3"
echo "Всего передано параметров: $#"
echo "Имя скрипта: $0"
echo "Значение переменной my_var: $my_var"
Запусти скрипт. Попробуй догадаться, что здесь происходит.
Запусти скрипт с параметрами, например: ~/bin/hello one two
Передай ему 1, 2 или 3 параметра, посмотри как меняется вывод.
Ввод данных
Можно запросить у пользователя ввод данных и записать их в переменную, это делает команда read
. Скрипт, который приветствует тебя по имени:
echo "Как тебя зовут?"
read MYNAME
echo "Привет, $MYNAME!"
Он печатает на экран фразу, затем ожидает ввода, и когда ты набираешь своё имя и нажимаешь Enter, он записывает его в переменную MYNAME и подставляет значение этой переменной во вторую фразу.
Условия
До этого времени мы говорили о простых скриптах, которые выполнялись последовательно. Но иногда бывает нужно, чтобы ход программы менялся в зависимости от каких-то условий. Чтобы изменить направление выполнения сценария, исходя из результатов проверки условия, нужно использовать следующую конструкцию:
if <условие>
then
<если условие верно, то выполняется этот блок>
else
<если условие не верно, то выполняется этот блок>
fi
Блок с else необязательный, его можно не использовать, если нам не нужно ничего делать в случае, если условие не выполняется.
Например, вот:
if cd $1
then
echo 'так так так посмотрим шо тут у нас'
ls -la
else
echo 'доступ видимо закрыт'
fi
Передаем на вход скрипту путь, если туда можно попасть, то вызывается команда ls, если нельзя, то сообщение о том, что скорее всего доступ закрыт.
Ещё можно тестировать файлы в скрипте с помощью [ ... ], например, здесь мы проверяем, что переданное аргументом имя файла существует:
if [ -e $1 ]
then
echo $1 существует
fi
Здесь можно найти все опции: https://shellmagic.xyz
Отступы здесь используются чисто для удобства.
Практика
Основное задание
Представь себе, что я заказчик и скидываю тебе примерное ТЗ (техническое задание) на выполнение. Я очень люблю удалять и часто случайно удаляю что-то не то, поэтому мне нужна команда, которая бы позволяла "безопасно" удалять файлы и директории, чтобы, если я спохватился, мог вернуть всё обратно. Я плохо понимаю в этих ваших линуксах, поэтому не могу четко сформулировать требования. По этой же причине мне нужна четкая инструкция по установке и описанию принципа работы этой команды, которая должна быть оформлена в репозитории.
Что нужно сделать
Реализовать команду srm
(safe rm) для безопасного удаление файлов. Команда работает как обычный rm
, то есть получает на вход путь до файла, но вместо удаления сжимает его утилитой gzip
и перемещает в директорию ~/RECYCLE
(корзина), в свою очередь из RECYCLE
автоматически удаляются файлы, которые находятся там больше семи дней. Удаление старых файлов должно активироваться после каждого вызова srm
.
Как к этому подходить
Создай пустой репозиторий с пустым файлом README.md (см предыдущий урок), для этого нужно просто нажать соответствующую галочку при создании
После создания, сделай новую ветку от мастера, можешь назвать её develop
и работай в ней
Раздели задачу на подзадачи и фиксируй выполнение каждой подзадачи коммитом. Например, сначала твой скрипт проверяет, существует ли директория ~/RECYCLE
, и если нет, то создаёт её — это первая подзадача, и так далее. Не забывай про правила оформления коммитов, это важно
Как всё будет готово, оформи README.md
с подробным описанием работы команды и, самое главное, установкой (чтобы заказчик мог её установить на свою систему)
После того, как всё будет готово, открывай пулл реквест на слияние ветки в мастер (инструкция есть в прошлом уроке). Удачи!
Задание* (со звёздочкой)
Реализовать команду для записи заметок в блокнот прямо внутри командной строки. Представь, что ты делаешь задания на курсе и не можешь отвлекаться, тебе в голову пришла какая-то очень крутая мысль и ты хочешь моментально её записать, чтобы не забыть. Записал не покидаю терминала и работаешь дальше. Красота!
Должно быть два режима — 1) ввод коротких (однострочных) заметок прямо на вход команде, в виде аргумента. 2) ввод длинных (многострочных) заметок, которые читают пользовательский ввод и ждут символа конца файла (нажатия Ctrl+D).
Файл заметок ~/NOTES
Реализовать вторую команду, которая будет искать из этого файла по ключевым словам, когда ты захочешь вспомнить что записывал.
Дополнительная практика
Задание 1
Скрипт для запуска гуся, на вход имя гуся! Если аргумент не передается, то остается дефолтное значение "РАБОТЯГИ", если передается имя, то оно заменяет дефолтное значение.
Шаблон:
ЗАПУСКАЕМ
░ГУСЯ░▄▀▀▀▄░РАБОТЯГИ░░
▄███▀░◐░░░▌░░░░░░░
░░░░▌░░░░░▐░░░░░░░
░░░░▐░░░░░▐░░░░░░░
░░░░▌░░░░░▐▄▄░░░░░
░░░░▌░░░░▄▀▒▒▀▀▀▀▄
░░░▐░░░░▐▒▒▒▒▒▒▒▒▀▀▄
░░░▐░░░░▐▄▒▒▒▒▒▒▒▒▒▒▀▄
░░░░▀▄░░░░▀▄▒▒▒▒▒▒▒▒▒▒▀▄
░░░░░░▀▄▄▄▄▄█▄▄▄▄▄▄▄▄▄▄▄▀▄
░░░░░░░░░░░▌▌▌▌░░░░░
░░░░░░░░░░░▌▌░▌▌░░░░░
░░░░░░░░░▄▄▌▌▄▌▌░░░░░
Задание 2
Скрипт принимает на вход текст, выводит его задом-наперед.
Задание 3
Скрипт, который находит в твоей домашней директории (и всех вложенных директориях) все файлы, которые весят больше 1 мегабайта. Вывод должен быть таким, чтобы его можно было передать дальше команде rm
и эти файлы удалились бы.
Задание 4
Скрипт вызывает команду last
и выдает топ-5 студентов, которые провели больше всех времени на сервере.
Задание 5
Скрипт считывает ~/.bash_history
и выдает 10 твоих самых часто используемых команд и количество использований.