[Перевод] Запросто собираем базу данных при помощи команд Linux

База данных — это сердце многих приложений, от полнофункциональных корпоративных сайтов до сравнительно простых инструментов, например, для ведения списков покупок и финансовых трекеров. Популярны реляционные базы данных на основе SQL, но в Linux можно собрать более простую и прозрачную альтернативную базу данных.

Базу данных какого рода можно собрать в Linux

В Linux доминируют текстовые файлы. В Linux есть экосистема и множество надёжных инструментов,  при помощи которых текстовые файлы удобно сцеплять — и очень многого добиться, оперируя такими файлами.

В частности, можно собрать базу данных, то есть, структурированный источник информации, который может пригодиться вам для очень многих вещей. Работая с текстовыми файлами и инструментами командной строки, можно создавать простые прототипы, быстро проверять ваши данные, а также версионировать данные столь же легко, как и любой код.

Какими инструментами можно воспользоваться?

В Linux есть много полезных команд, в том числе, для работы с текстом. Многие из этих команд действуют как фильтры, получающие данные через стандартный ввод, выполняющие над этими данными некоторые операции и выдающие стандартный вывод.

  • grep обеспечивает поиск по вводимой информации и позволяет выбрать те строки, которые соответствуют одному или нескольким шаблонам.

  • cut извлекает избранные фрагменты каждой строки и записывает их в стандартный вывод.

  • awk — более мощный язык для сканирования и обработки паттернов.

  • sort выполняет именно сортировку (как и следовало ожидать), но может сортировать данные и по конкретным столбцам, а также корректно справляется с числовой/алфавитной сортировкой.

  • При помощи команд head и tail можно извлекать из вывода заданный срез строк.

  • join поддерживает взаимосвязанные данные, расположенные во множестве файлов.

У вас есть инструментарий Linux — как с его помощью создать и использовать базу данных

В данном примере мы создадим простую базу данных для приложения из разряда «список дел». Весь базовый функционал такого приложения можно реализовать при помощи стандартных инструментов Linux. В дальнейшем возможности этого приложения можно будет расширить при помощи скриптового языка, либо перенеся его на реляционную базу данных.

Создание таблиц в виде двумерных файлов

Один из простейших структурированных текстовых форматов называется DSV или значения, разделённые разделителем. Это более общий случай формата CSV —значений, разделённых запятыми. В структурированных текстовых файлах под Linux в качестве разделителя полей часто используются пробел или двоеточие. Классический пример — файл  /etc/passwd:

ad26bb3c989e85a40e8abf5718dca336.png

В таком формате можно хранить разнообразные данные, в том числе, и список дел:

Buy milk:2024-10-21:2:open
Call bank:2024-10-20:1:closed

Для обновления базы данных подойдёт любой текстовый редактор — в этом заключается ещё одно достоинство обычного текста. Можно добавлять элементы непосредственно из командной строки, перенаправляя вывод из echo в файл:

echo "Take out the trash:$(date -I):3:open" > tasks

Вот эквивалентный код на SQL:

INSERT INTO tasks VALUES('Take out the trash', CURDATE(), '3', 'open')

Обратите внимание: для получения актуальной даты в этой команде используется подкоманда. Немного неудобно прописывать её руками, значительно удобнее было бы автоматизировать её при помощи скрипта.

Выборка целой таблицы

Выбор данных — пожалуй, самая типичная задача при работе с базой данных. Самый простой вариант — выбрать из таблицы всю информацию, то есть

SELECT * FROM tasks

Такой командой мы извлекаем все столбцы базы данных на высоту всех строк. Если мы имеем дело с файловой базой данных, то эквивалентная команда также тривиальна:

cat tasks

40cd19dd80715056f288ba154e221b78.png

Выбор столбцов при помощи cut

Чуть более искусная операция — сузить выборку до конкретных столбцов. Вот как это делается в SQL:

SELECT task FROM tasks

При помощи инструмента cut можно реализовать практически тот же самый функционал:

cut -d':' -f1 tasks

При помощи опции d задаём разделитель — тот знак, который будет ставиться между полями в каждой строке вашего файла. При помощи опции f выбираем конкретные поля. Следующий корд позволяет вывести список всех задач, содержащихся в вашей базе данных:

056734c445d2ea48b05018bc0edff57e.png

Выбор строк при помощи grep или awk

Обычно требуется вытащить из базы данных не все строки, а как-то ограничить результаты. В таких случаях наиболее распространено требование отфильтровать содержимое по значениям, как, например, здесь:

SELECT * FROM tasks WHERE status=open

В данном случае grep отлично нам подойдёт. Воспользовавшись этой командой, можно сравнивать строки с шаблоном, заданным в виде регулярного выражения. Таким образом можно найти, например, все задачи, имеющие статус «open» (открыта):

grep 'open$' tasks

ca67a8760318bcd6cdceafa3328f81ee.png

В данном случае мы опираемся на тот факт, что каждая строка оканчивается полем «status»; знак $ — это конец строки. Что касается полей в середине строки, для их обработки может потребоваться более сложное регулярное выражение. Например, вот как можно получить все строки с приоритетом 2:

grep ':2:[^:]*$' tasks

Но grep обеспечивает сопоставление только с текстовыми шаблонами и не справляется с более сложными выражениями, например, с такими:

SELECT status, task FROM tasks WHERE date<2024-10-21

В этом коде SQL при помощи логического сравнения мы получаем задачи, заведённые до определённой даты. Можно попытаться собрать более сложное регулярное выражение, но так мы рискуем выйти за рамки возможностей grep.

В данном случае вам потребуется более сложный инструмент, такой как awk:

awk -F':' '$2<"2024-10-21" {print $1 ":" $2 }' tasks

efd7dcf4dde3768c32f93a4dc6b4eca3.png

Awk может справиться одновременно с задачами grep и cut. В данном примере часть

$2<"2024-10-21"

— это предусловие, означающее, что шаблону соответствуют лишь значения с более ранней датой. После этого команда выведет на экран первые два столбца из каждой строки.

Постраничное разбиение результатов при помощи head и tail

В языке SQL предусмотрен оператор LIMIT, при помощи которого можно выбирать конкретное количество элементов из результатов. Вот как выбрать первые две строки:

head -2 tasks

При помощи tail можно получить n последних строк. В совокупности с head мы воспроизводим функционал оператора LIMIT, причём, включая сдвиги. Например, вот как получить строки 2–3:

head -3 tasks | tail -2

411f15cdb674c06ac7765fc2886948a4.png

Сортировка строк при помощи sort

Во многих SQL-командах важная роль отводится оператору ORDER BY. К счастью, в Linux есть отличная эквивалентная команда sort. Подобно cut и awk, можно указать разделительный знак и поле по номеру, хотя, флагам соответствуют разные буквы. На этот раз t — это разделитель, а k — номер поля:

sort -t':' -k2 tasks

Далее будут отображены все поля, отсортированные по дате:

9886c007c50177fcdebffb0685ab9de0.png

Объединение таблиц при помощи join

Суть реляционных баз данных заключается в описании отношений между различными таблицами, где поле из одной ссылается на поле из другой. Возможно, вы раньше не знали, что в Linux есть команда, эквивалентная оператору JOIN из языка SQL — неудивительно, что называется она join.

Давайте расширим данные списка дел так, чтобы в нём можно было учитывать задачи для нескольких пользователей. Для начала добавим в исходный файл задач новый столбец name, так, чтобы данные приняли следующий вид:

f45491a3655bc31cd7c55b1e666deca5.png

Затем создадим файл people, в котором будем хранить данные по каждой персоне, чьи задачи мы учитываем:

e58150d106ec8dc4ef398d2c81d81c3d.png

Теперь можно воспользоваться командой join с разделителем, который указывается через опцию t:

join -t':' -1 5 -2 1 tasks people

При помощи опций -1 и -2 указываем номера тех полей из каждого файла, которые мы собираемся объединять. Здесь речь идёт о первом и пятом полях соответственно. Команда join будет использовать первое поле по умолчанию, так что код можно упростить до:

join -t':' -1 5 tasks people

В результате получим:

4686ab227625b3eed0a49b28222aede9.png

Чтобы сделать вывод немного чище, можно конвейеризовать объединённые таблицы. В таком случае получится обрезать и опустить поле name:

join -t':' -1 5 tasks people | cut -d':' -f2-

a784a440db48e9020a0acdefabe75b09.png

Кроме того, можно будет объединить два имени в одно при помощи awk:

join -t':' -1 5 tasks people | awk -F':' '{print $2":"$3":"$4":"$5":"$6" "$7}'

ba9f741be070ae00521361514ae406f0.png

Всё вместе

В заключение давайте рассмотрим гораздо более сложное выражение на языке SQL. Вот выражение, при помощи которого мы объединяем обе таблицы, чтобы получить имена, а также выбрать конкретные столбцы и выбрать строки с определённым приоритетом. Далее выполняется сортировка по дате и выбирается только первая подходящая строка:

SELECT task,date,priority,status,first_name,last_name
FROM tasks t
LEFT JOIN people p ON t.name=p.name
WHERE priority=2
ORDER BY date
LIMIT 1

Эквивалентный конвейер команд, пожалуй, несколько сложнее понять, но и в этом нет ничего трудного, если вы знакомы со следующими ключевыми инструментами:

join -t':' -1 5 -2 1 tasks people \
  | awk -F':' '{print $2":"$3":"$4":"$5":"$6" "$7}' \
  | grep ':2:' \
  | sort -t ':' -k2 \
  | head -1

fb329200330a0bfbaf995454b724fb39.png

© Habrahabr.ru