База данных — это сердце многих приложений, от полнофункциональных корпоративных сайтов до сравнительно простых инструментов, например, для ведения списков покупок и финансовых трекеров. Популярны реляционные базы данных на основе SQL, но в Linux можно собрать более простую и прозрачную альтернативную базу данных.
Базу данных какого рода можно собрать в Linux
В Linux доминируют текстовые файлы. В Linux есть экосистема и множество надёжных инструментов, при помощи которых текстовые файлы удобно сцеплять — и очень многого добиться, оперируя такими файлами.
В частности, можно собрать базу данных, то есть структурированный источник информации, который может пригодиться вам для очень многих вещей. Работая с текстовыми файлами и инструментами командной строки, можно создавать простые прототипы, быстро проверять ваши данные, а также версионировать данные столь же легко, как и любой код.
Какими инструментами можно воспользоваться?
В Linux есть много полезных команд, в том числе для работы с текстом. Многие из этих команд действуют как фильтры, получающие данные через стандартный ввод, выполняющие над этими данными некоторые операции и выдающие стандартный вывод.
grep обеспечивает поиск по вводимой информации и позволяет выбрать те строки, которые соответствуют одному или нескольким шаблонам.
cut извлекает избранные фрагменты каждой строки и записывает их в стандартный вывод.
awk — более мощный язык для сканирования и обработки паттернов.
sort выполняет именно сортировку (как и следовало ожидать), но может сортировать данные и по конкретным столбцам, а также корректно справляется с числовой/алфавитной сортировкой.
При помощи команд head и tail можно извлекать из вывода заданный срез строк.
join поддерживает взаимосвязанные данные, расположенные во множестве файлов.
У вас есть инструментарий Linux — как с его помощью создать и использовать базу данных
В данном примере мы создадим простую базу данных для приложения из разряда «список дел». Весь базовый функционал такого приложения можно реализовать при помощи стандартных инструментов Linux. В дальнейшем возможности этого приложения можно будет расширить при помощи скриптового языка, либо перенеся его на реляционную базу данных.
Создание таблиц в виде двумерных файлов
Один из простейших структурированных текстовых форматов называется DSV или значения, разделённые разделителем. Это более общий случай формата CSV —значений, разделённых запятыми. В структурированных текстовых файлах под Linux в качестве разделителя полей часто используются пробел или двоеточие. Классический пример — файл /etc/passwd:

В таком формате можно хранить разнообразные данные, в том числе и список дел:
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
Выбор столбцов при помощи cut
Чуть более искусная операция — сузить выборку до конкретных столбцов. Вот как это делается в SQL:
SELECT task FROM tasksПри помощи инструмента cut можно реализовать практически тот же самый функционал:
cut -d':' -f1 tasksПри помощи опции d задаём разделитель — тот знак, который будет ставиться между полями в каждой строке вашего файла. При помощи опции f выбираем конкретные поля. Следующий корд позволяет вывести список всех задач, содержащихся в вашей базе данных:

Выбор строк при помощи grep или awk
Обычно требуется вытащить из базы данных не все строки, а как-то ограничить результаты. В таких случаях наиболее распространено требование отфильтровать содержимое по значениям, как, например, здесь:
SELECT * FROM tasks WHERE status=openВ данном случае grep отлично нам подойдёт. Воспользовавшись этой командой, можно сравнивать строки с шаблоном, заданным в виде регулярного выражения. Таким образом можно найти, например, все задачи, имею��ие статус "open" (открыта):
grep 'open$' tasks
В данном случае мы опираемся на тот факт, что каждая строка оканчивается полем «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
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
Сортировка строк при помощи sort
Во многих SQL-командах важная роль отводится оператору ORDER BY. К счастью, в Linux есть отличная эквивалентная команда sort. Подобно cut и awk, можно указать разделительный знак и поле по номеру, хотя флагам соответствуют разные буквы. На этот раз t — это разделитель, а k — номер поля:
sort -t':' -k2 tasksДалее будут отображены все поля, отсортированные по дате:

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

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

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

Чтобы сделать вывод немного чище, можно конвейеризовать объединённые таблицы. В таком случае получится обрезать и опустить поле name:
join -t':' -1 5 tasks people | cut -d':' -f2-
Кроме того, можно будет объединить два имени в одно при помощи awk:
join -t':' -1 5 tasks people | awk -F':' '{print $2":"$3":"$4":"$5":"$6" "$7}'
Всё вместе
В заключение давайте рассмотрим гораздо более сложное выражение на языке 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