habrahabr

JSON pipes в шелле

  • понедельник, 2 июня 2014 г. в 03:10:24
http://habrahabr.ru/post/102072/

Чем больше я пишу однострочники в шелле, тем больше я прихожу к двум важным идеям:
  1. Это очень мощное средство для «непосредственного программирования», то есть указания компьютеру, что делать.
  2. Большая часть однострочника посвящена grep/awk/cut/tr, которые каким-то образом выковыривают и приводят в человеческий вид вывод предыдущих утилит.

При том, что модель pipe'ов восхитительна, совершенно грязные хаки по отлову нужных полей в выводе во втором пункте («а вот тут мы можем выделить нужное нам по характерной запятой с помощью awk -F, '{print $2}'...) делают процедуру спорной по удовольствию, и уж точно нечитаемой.

Ещё одна серьёзная проблема: при том, что шелл даёт довольно много идиом из функционального программирования, в нём нет идиомы фильтрации списка по результату выполнения внешней программы. То есть „грепнуть“ список мы можем. А вот оставить в списке только те элементы, для которых какая-то программа вернула „успех“ — нет.

При этом есть враждебная и не очень хорошо написанная среда — powershell (винды). В которых взяли хорошую идею (пайпы передают не текст, а объекты), но испортили её двумя вещами:
  1. Неэргономичной консолью виндов (Shift-PgUp где, а? говорят, Ctrl-PdUp в новых версиях)
  2. предложением пойти и выучить .net для того, чтобы нормально с методами работать.
  3. Отсутствием под большинство операционных систем


Хочется иметь объекты в пайпе в тёплом ламповом линуксовом шелле. С hand-candy (мало печатать), eye-candy (приятно смотреть) и общей эргономичностью процесса использования. Ещё хочется иметь возможность сочетать „новый подход“ со старым, то есть обычным текстовым pipe'ом.

Идея


Надо написать набор инструментов, которые позволят в pipe-style оперировать с структурированными данными. Очевидным выбором является XML JSON.
Нам нужно:
  1. Утилиты, которые примут типовые форматы на вход и сконвертируют их в json.
  2. Утилиты, которые позволят в pipe'е манипулировать с json'ом.
  3. Утилиты, которые приведут 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}dal\d*' | 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


Как подсказывают в привате, mysql-json отлично ложится на эту идею. Запускать бинарники по выводу из sql'я? Запросто.

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'а всё-таки более предсказуема, чем обработка «типа тут пробелами элементы разделяются» в существующем шелле.

Реализация


Я бы сам написал, но часть нужного я не знаю, на что-то мне не хватает времени. Не программист, я. Тайной мыслью статьи является, что «кто-то напишет за меня», но если такого не найдётся, то останется хотя бы программная статья с мотивацией доучить(ся) и сделать самому.

Если кто-то готов за подобное взяться — буду крайне благодарен. Если нет, буду свой фиговый питон расчехлять — и идеи и предложения приветствуются.