§ 3. Скрипты
Автоматически запускаемые скрипты
01 При каждом новом запуске оболочки в интерактивном режиме автоматически выполняются скрипты. Если оболочка запущена с флагом -l, то выполняется скрипт ~/.bash_profile, иначе выполняется скрипт ~/.bashrc. Флаг -l используется командой login и обозначает первичный запуск оболочки при входе в систему, так что в файле ~/.bash_profile обычно пишут команды, которые нужно запустить только один раз. Также при завершении оболочки, запущенной с флагом -l выполняются команды из файла ~/.bash_logout. Если вы хотите автоматически запускать команды из ~/.bashrc также при входе в систему, то внутри ~/.bash_profile нужно добавить команду для включения в него файла ~/.bashrc.
test -f ~/.bashrc && source ~/.bashrc
Эта команда проверит, что файл существует, и выполнит все команды из этого файла, как будто они были написаны внутри текущего файла.
| Файл | Когда используется |
|---|---|
| ~/.bash_profile | запуск bash -l |
| ~/.bash_logout | завершение bash -l |
| ~/.bashrc | запуск bash без -l |
02 Внутри автоматически запускаемых скриптов обычно пишут команды, упрощающие работу с оболочкой. Например, если вы храните свои вспомогательные скрипты в директории ~/.local/bin, то вы можете добавить ее в переменную PATH, чтобы оболочка искала команды в этой директории наравне с системными директориями. Если вы замечаете, что постоянно набираете одни и те же флаги для какой-либо команды, то вы можете сделать для нее псевдоним с помощью команды alias, в котором укажете эти флаги. Тогда в интерактивном режиме они всегда будут добавляться к вашей команде. Также в bash можно настраивать внутренние опции посредством переменных.
# переменные среды export PATH=$PATH:$HOME/.local/bin # теперь можно использовать команды из директории ~/.local/bin export EDITOR=nano # теперь команды, запускающие редактор, будут использовать nano # псевдонимы alias rm='rm --one-file-system --preserve-root' # безопасный вариант команды rm alias ls='ls --color=auto --group-directories-first' # вариант команды ls с цветным выводом alias ip='ip -brief -color' # краткий вариант команды ip alias mkdir='mkdir -p' # теперь команда mkdir создает директории рекурсивно alias cal='cal --monday' # теперь в выводе команды cal неделя начинается с понедельника # настройки bash PS1='🌊 ' # приглашение #PS1='\u@\h \w\$ ' # стандартное приглашение HISTCONTROL=ignoreboth:erasedups # не записывать в историю команды, начинающиеся с пробела и дубликаты HISTSIZE=100000 # запоминать последнии сто тысяч команд # вставка пользовательского файла с настройками test -f ~/.bashrc && source ~/.bashrc
Отладка скриптов
03 Из-за сложных правил обработки пробелов и кавычек сложно понять, как произошла ошибка. Для решения этой проблемы обычно в начало скрипта добавляют опцию set -x. После включения этой опции оболочка будет выводить каждую команду, которую она запускает со списком фактических аргументов (с раскрытыми подстановками переменных и т.п.). Также для скриптов полезна опция set -e, которая остановит выполнение скрипта, как только какая-либо команда вернет ненулевое значение.
04 Для проверки корректности работы с подстановками и корректности использования стандарта POSIX sh, существует команда и веб-сервис shellcheck. Ниже показан пример работы команды на одном из скриптов.
$ cat my-script #!/bin/sh for i in 1 2 3; do echo "$i" "$x"; done $ shellcheck my-script In my-script line 2: for i in 1 2 3; do echo "$i" "$x"; done ^-- SC2154: x is referenced but not assigned. For more information: https://www.shellcheck.net/wiki/SC2154 -- x is referenced but not assigned.
Функции
05 Внутри скриптов можно создавать функции, которые ведут себя как встроенные скрипты: внутри них переопределяются переменные-аргументы $1, $2 и так далее, а вместо exit используется return. Функции удобно в больших скриптах, где вы хотите избежать повторения кода или хотите сгруппировать код в логические единицы.
hello() { echo "Hello $1" } hello "X" # выведет Hello X
Генерация потока ввода
06 Многие команды работают со стандартным потоком ввода, но не с аргументами, из-за чего возникает необходимость использовать подстановки переменных внутри потока ввода. Для этого используются генераторы потока ввода, которые по английски называются here-strings. Они также удобны для генерации конфигурационных и иных файлов.
x=1 y=2 # подстановка разрешена, получится файл # x = 1 # y = 2 cat > my.conf << EOF x = $x y = $y EOF # подстановка не разрешена, получится файл # x = 1 # y = 2 cat > my.conf << EOF x = $x y = $y EOF
Задания
Открытие файлов 1 балл
07 Напишите скрипт под названием open, который открывает файл в подходящей программе. Программа выбирается на основе MIME-типа файла. Этот тип определяется с помощью команды file, и вам нужно найти подходящий для этого флаг в документации. Затем для открытия файла используется программа, которая умеет работать с такими типами файлов: gimp, libreoffice, mpv. Тестовые файлы можно скачать здесь. Если вы делаете задание на сервере, то вместо запуска программы выведите нужную для запуска команду. Для выбора программы используйте условные переходы (case или if).
Аргументы 1 балл
08 Напишите скрипт, который принимает опции -x, -y и выводит погоду в точке с долготой X и широтой Y. Для определения погоды, используйте сайт wttr.in. Для обработки аргументов используйте встроенную функцию getopts (не перепутайте ее с getopt). Пример использования этой функции ниже.
# двоеточие после опции обозначает, что у нее есть значение, а не просто вкл./выкл. while getopts a:b: option do case "$option" in a echo "a=$OPTARG" ;; b) echo "b=$OPTARG" ;; *) echo "no options" ;; esac done
Функции 1 балл
09 Напишите функцию, которая запускает переданную в нее команду и перенаправляет стандартный поток вывода и поток ошибок в файл. Название файла передается в качестве первого аргумента, остальные аргументы — это команда. Пример вызова ниже.
my_func /tmp/my-output ls -l
Генерация файлов 2 балла
10 Напишите скрипт, который генерирует еще один скрипт, который переименовывает все файлы в директории, меняя регистр на прописные буквы. Для генерации скрипта используйте генерацию потока ввода. Для изменения регистра используйте команду tr. Для переименования файлов используйте команду mv. Список файлов должен вставляться в скрипт с помощью подстановки во время генерации потока ввода. Пример результирующего скрипта показан ниже. Вам может пригодиться перенаправление потока вывода, которое не добавляет данные в конец файла, а не перезаписывает его. Оно реализуется с помощью символов >>.
mv x X mv y Y mv z Z