Обработка текстов и сценарии
Более плотная лекция 2020 года на похожую тему
Задача склейки
Командная строка как основной интерфейс управления
⇒ Понятие сценария:
- Использование тех же самых команд
- Алгоритмическая полнота (по возможности)
- Удобная суперпозиция команд (подстановка)
- «Админское программирование»: экономия объёма получающегося текста программы
Почему это часто встречается при сборке? Рецепты в make — это именно сценарии shell!
Простые команды, которые проще написать на шелле, чем городить отдельный синтаксис make
- Нестандартные сценарии и рецепты
- Костыли
Shell
- Интерпретатор командной строки
- редактор командной строки, история, достраивание, alias-ы, …, управление заданиями и многое другое
- ЯП ВУ (функции, условные операторы и циклы, в некоторых шеллах — массивы)
Ориентация на работу с текстом / файлами / процессами — склейку
Глава из учебника про shell
(Интерпретатор: редактор командной строки, история, достраивание, alias-ы, …, управление заданиями)
ЯП с упором на склейку
TODO примеры
- Переменные и их подстановка с простейшей пост-обработкой
Встроенные команды и программы-команды
- ⇒ «Сколько команд в командной строке»?
⇒ управление процессами (& и wait)
- Перенаправление ввода-вывода
Вывод в строковое выражение `команда`, оно же $(команда)
>, < и 2>
>> и <<
|, великий и могучий!
- Условный оператор и циклы
- Exit status как условие
⇒ Утилита test (она же — утилита [)
case и шаблоны
Базовая арифметика $((expr))
- Генерация имён файлов
- Обработка сигналов (см. пример ниже)
- …
Утилиты для работы с файловой системой и процессами
Основная документация: GNU coreutils
- всякая классика типа cp/mv/ls и т. п.
Пример: как гарантировано удалять временные файлы:
1 #!/bin/sh 2 exit_handler() { 3 trap - EXIT 4 test -r "$TMPFILE" && rm -vf "$TMPFILE" 5 } 6 7 trap exit_handler EXIT HUP INT QUIT PIPE TERM 8 9 echo -n "Create a file? " 10 read YN 11 if [ "$YN" = y ]; then 12 TMPFILE="`mktemp`" 13 echo "$TMPFILE" 14 ls -l "$TMPFILE" 15 fi 16 echo -n "Wait…" 17 read 18 echo "Done"
- …
Утилиты работы с текстом
Проблема локали:
Основная документация: GNU coreutils
cut, tr, tail, head, sort, paste, join, их много… (coreutils)
Пример: join
hexdump из Util-linux, xxd из vim и od из coreutils
Just for fun: moreutils
- …
Комбайны
grep для поиска
sed для поиска с заменой:
$ cal | sed 's/\([01]\)\([2-3]\)/\2:\1/g' Октябрь 22:01 Пн Вт Ср Чт Пт Сб Вс 1 2 3 4 5 6 7 8 9 10 11 2:1 3:1 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
awk (или perl) для написания программ, управляемых контекстом
можно и sed, но…
а можно и любой ЯП. но будет длинно
m4…
- …
Д/З
Прочитать и попробовать всё, что вам покажется релевантным задаче из GNU coreutils, возможно, больше
В репозитории с Д/З сделать (вложенный) подкаталог 03_Text, а в нём — сценарий на shell randomize.sh, который принимает один необязательный параметр — время задержки в секундах (вещественное) перед выводом очередного символа, затем читает со стандартного ввода небольшой файл с ASCII-артом в кодировке ASCII и без символов табуляции, после чего выводит его посимвольно в случайном порядке в левый верхний угол экрана. Должен получиться с виду тот же ASCII-арт (аккуратнее с пробелами).
Что может понадобиться:
- Перенаправление ввода-вывода
tput (cup и clear, было на лекции) из пакета termutils
Арифметика в shell (конструкция вида $((…)))
Параметры командной строки shell-сценария (конструкция вида $1, $2 и т. п.)
Цикл while в shell
Условие: нельзя пользоваться другими языками программирования, кроме shell (sed-ом можно, но можно обойтись и без него).
Необязательное дополнение: скрипт должен выполняться стандартным shell-ом (не bash, а dash, он же ash, например). Я пока нашёл одну неприятность; встроенное echo в ash не умеет -e — пользуйтесь /bin/echo, оно умеет
Необязательное дополнение: научиться работать с псевдографикой и русскими буквами.
- Проблема: русские буквы в UTF8 занимают два байта, а псевдографика — вообще три
Решение: сначала перекодировать поток (iconv) в UCS2, например, там всё будет одинаковое
Необязательное дополнение: выводить ASCII-арт посередине экрана
Проблема (неожиданная): посчитать во входном потоке количество строк и максимальную ширину строки легко, а вот передать эти данные перед тем, как передать остальные, данные просто так не получится
Размер экрана — stty (причём поскольку стандартный ввод — это не терминал, то конкретно stty size < /dev/tty)
Решение: использовать временный файл (mktemp) — только обязательно удалять его
Я решал задачу так (спойлер на русском, осталось только перевести на shell ☺):
Базовое решение:
Вариант 2021 года:
Вариант этого года (рамочка и фон рисуются с помощью dialog; да, она должна быть на 1 больше, но мне понравилось, что месяц отображается в виде заголовка ☺); всего менее 40 строк кода
Посчитал размер экрана с помощью stty, и высоту / максимальную ширину вывода с помощью wc
Вывел рамочку нужного размера с помощью dialog; подстроил координаты, чтобы вывод попадал в эту рамочку
Установил цвет фона с помощью tput setab (такой же, как у внутренности рамочки -серый)
Преобразовал текст в кодировку UCS2 с помощью iconv
Преобразовал текст в последовательность двухбайтовых восьмеричных чисел с помощью od
(while read) считывал ввод в цикле построчно (в две переменные)
(case) пробелы игнорировал, при переводе строки обнулял X, но увеличивал Y, в остальных случаях увеличивал X
(echo) каждую пару числе вывел, сопровождая полученными координатами X и Y
Перемешал вывод с помощью shuf
(while read) читал уже перемешанные X, Y и два числа-байта
С помощью tput cup) позиционировал курсор и (echo -ne) выводил два байта символа (пригодилась конструкция «\0…») с перекодировкой обратно в UTF
(sleep) ждал после каждого вывода