Чем больше я пишу однострочники в шелле, тем больше я прихожу к двум важным идеям:
- Это очень мощное средство для «непосредственного программирования», то есть указания компьютеру, что делать.
- Большая часть однострочника посвящена grep/awk/cut/tr, которые каким-то образом выковыривают и приводят в человеческий вид вывод предыдущих утилит.
При том, что модель pipe'ов восхитительна, совершенно грязные хаки по отлову нужных полей в выводе во втором пункте («а вот тут мы можем выделить нужное нам по характерной запятой с помощью awk -F, '{print $2}'...) делают процедуру спорной по удовольствию, и уж точно нечитаемой.
Ещё одна серьёзная проблема: при том, что шелл даёт довольно много идиом из функционального программирования, в нём нет идиомы фильтрации списка по результату выполнения внешней программы. То есть „грепнуть“ список мы можем. А вот оставить в списке только те элементы, для которых какая-то программа вернула „успех“ — нет.
При этом есть враждебная и не очень хорошо написанная среда — powershell (винды). В которых взяли хорошую идею (пайпы передают не текст, а объекты), но испортили её двумя вещами:
- Неэргономичной консолью виндов (
Shift-PgUp где, а?говорят, Ctrl-PdUp в новых версиях) - предложением пойти и выучить .net для того, чтобы нормально с методами работать.
- Отсутствием под большинство операционных систем
Хочется иметь объекты в пайпе в тёплом ламповом линуксовом шелле. С hand-candy (мало печатать), eye-candy (приятно смотреть) и общей эргономичностью процесса использования. Ещё хочется иметь возможность сочетать „новый подход“ со старым, то есть обычным текстовым pipe'ом.
Идея
Надо написать набор инструментов, которые позволят в pipe-style оперировать с структурированными данными. Очевидным выбором является XML JSON.
Нам нужно:
- Утилиты, которые примут типовые форматы на вход и сконвертируют их в json.
- Утилиты, которые позволят в pipe'е манипулировать с json'ом.
- Утилиты, которые приведут json в „обычный“ формат.
В этом случае человек не будет видеть json на экране, но будет иметь возможность работать с ним.
Для затравки
(для понимания я буду писать длинные имена утилит, в реальной жизни это будут короткие сокращения, то есть не json-get-object, а что-то типа jgo или jg)
Выводит только файлы, для которых file сумел определить тип:
ls -la | ls2json | json-filter 'filename' --exec 'file {} >/dev/null' | json-print
Выкачивает с некоторого сайта токен для авторизации, выковыривает его из json'а и выставляет в переменные среды окружения, после чего скачивает список и отфильтровав по регэкспу поле „автор“ выкачивает все url'ы:
curl mysite/api.json | env `json-get-to-env X-AUTH-TOKEN`;curl -H X-AUTH-TOKEN $X-AUTH-TOKEN mysite/api/list.json | json-filter --field 'author' --rmatch 'R.{1,2}dald*' | json-get --field 'url' | xargs wget
Парсит вывод find -ls, сортирует по полю size, вырезает из массива элементы с 10 по 20, выводит их в csv.
find . -ls | ls2josn | json-sort --field 'size' | json-slice [10:20] | json2csv
Терминология
input'ы
Основная задача — из messy-вывода сделать json-конфетку. Важно: иметь опцию для обработки некорректного ввода: а) игнорировать, б) останавливать pipe с ошибкой.
Примеры:
Generic:
- line2json — конвертирует обычный вывод в массив строк, где строка соответствует строке (line to string).
- words2json — аналогично, но по „словам“.
- csv2json — конвертирует cvs в объект, позволяя указанный элемент назначить ключом.
- lineparse2json — конвертирует строку в объект, разделяя её по указанным символам. Напоминает awk -F: '{print $1, $2}',
app-specific:
- ls2json (на выбор — либо делает ls, либо берёт вывод ls) и структурирует его как массив объектов, где каждый объект — файл с кучей полей. Может быть, даже большей, чем умеет ls (обычные и расширенные атрибуты lsattr, вся информация про иноды, даты создания и т.д.)
- ps2json — аналогично, по спискам процессов
- lsof2json — список объектов, описывающих приложения, использующие файл.
- openfiles2json — список fd, открытых приложением (/proc/PID/fd), с встроенной фильтрацией, например, „files only“, „ignore /dev/null“. В объектах по сетевым сокетам сразу же прилагается вся информация — порты/ip.
- iptables2json — выводит текущие настройки iptables в форме json
File-specific:
Читают файл, выводят его в json'е.
- syslog2json
- ini2json
- conf.d2json
- sysv2json, upstart2json
нативные json-преобразования
Самое вкусное — нативные манипуляции над json'ом. Аналогично, должны иметь опции обработки „не json'а — “игнорировать»/«останавливаться».
- json-filter — фильтрует объекты/массивы по заданным критериям.
- json-join — делает из двух json'ов один указанным методом.
- json-sort — сортирует массив объектов по указанному полю
- json-slice — вырезает кусочек из массива
- json-arr-get — возвращает элемент из массива
- json-obj-get — возвращает заданное поле/поля указанного объекта
- json-obj-add — добавляет объект
- json-obj-del — удаляет объект
- json-obj-to-arr — выводит ключи или заданное поле объектов как массив
- json-arr-to-obj — превращает массив в объект формируя ключ по заданному признаку.
- json-uniq — удаляет повторяющиеся элементы в массиве (или выводит только повторяющиеся)
(добавить по вкусу и потребностям)
output'ы
Приводят json в человекочитаемый вид:
- json2fullpath — превращают json в нотацию строк вида key1.key2[3].key4 = «foobar»
- json2csv
- json2lines — выводят массив по элементу на строку, если внутри объекты — разделяя их пробелами на строке.
- json2keys — выводит ключи объекта
- json2values — выводит только значения объекта
iterator'ы
Фактически, расширение xargs на json:
- json-keys-iterate — запускает указанные команды для каждого ключа
- json-values-iterate — запускает указанные команды для каждого ключа
- json-iterate — запускает указанные команды для каждого элемента
Сложности
Разумеется, такими методами невозможно решить проблему обработки произвольного json'а — он может оказаться слишком «неструктурированным». Но во-первых input'ы делают таки предсказуемый вид json'а, а во-вторых, обработка json'а всё-таки более предсказуема, чем обработка «типа тут пробелами элементы разделяются» в существующем шелле.
Реализация
Я бы сам написал, но часть нужного я не знаю, на что-то мне не хватает времени. Не программист, я. Тайной мыслью статьи является, что «кто-то напишет за меня», но если такого не найдётся, то останется хотя бы программная статья с мотивацией доучить(ся) и сделать самому.
Если кто-то готов за подобное взяться — буду крайне благодарен. Если нет, буду свой фиговый питон расчехлять — и идеи и предложения приветствуются.
Автор: amarao