§ 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
Файл ~/.bash_profile.

Отладка скриптов

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

Видео

Запись лекции 18.09.2021.