Вопрос или проблема
Я пытаюсь получить список установленных пакетов с версиями в легко обрабатываемом формате для автоматического анализа инструментом.
Я могу получить следующий вывод:
/ # apk list -i
WARNING: Ignoring APKINDEX.00740ba1.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.d8b2a6f4.tar.gz: No such file or directory
musl-1.1.22-r2 x86_64 {musl} (MIT) [installed]
zlib-1.2.11-r1 x86_64 {zlib} (zlib) [installed]
apk-tools-2.10.4-r2 x86_64 {apk-tools} (GPL2) [installed]
musl-utils-1.1.22-r2 x86_64 {musl} (MIT BSD GPL2+) [installed]
libssl1.1-1.1.1c-r0 x86_64 {openssl} (OpenSSL) [installed]
alpine-baselayout-3.1.2-r0 x86_64 {alpine-baselayout} (GPL-2.0-only) [installed]
alpine-keys-2.1-r2 x86_64 {alpine-keys} (MIT) [installed]
busybox-1.30.1-r2 x86_64 {busybox} (GPL-2.0) [installed]
scanelf-1.2.3-r0 x86_64 {pax-utils} (GPL-2.0) [installed]
libc-utils-0.7.1-r0 x86_64 {libc-dev} (BSD) [installed]
libtls-standalone-2.9.1-r0 x86_64 {libtls-standalone} (ISC) [installed]
ssl_client-1.30.1-r2 x86_64 {busybox} (GPL-2.0) [installed]
ca-certificates-cacert-20190108-r0 x86_64 {ca-certificates} (MPL-2.0 GPL-2.0-or-later) [installed]
libcrypto1.1-1.1.1c-r0 x86_64 {openssl} (OpenSSL) [installed]
Однако, имена пакетов могут содержать дефисы, а версии отделяются дефисами, что делает невозможным надежный разбор этого результата.
Есть ли способ получить эту информацию в формате, который легче разбирать? JSON был бы замечателен, но простая таблица, разделенная табуляцией, тоже подойдёт.
Спасибо!
Однако, имена пакетов могут содержать дефисы, а версии отделяются дефисами, что делает невозможным надежный разбор этого результата.
Версии сами по себе не могут содержать дефисы – всегда есть ровно одна группа для поля pkgver и ровно одна группа для pkgrel. Поэтому на самом деле очень легко разобрать, отделив последние две группы, разделенные дефисами; имя пакета – это то, что остается.
Большинство языков программирования могут это сделать, используя либо регулярные выражения, либо функции типа .rsplit() / .rindex().
Например, этот PCRE regex совпадет со всеми полями:
/(.+)-([^-]+)-r([^-]+) (\S+) \{(\S+)\} \((.+?)\)/
Чтобы сгенерировать что-то вроде JSON на выходе, используйте:
apk list -i | perl -MJSON -E 'print encode_json([map {
@foo{qw(pkgname pkgver pkgrel arch group license)} =
/(.+)-([^-]+)-r([^-]+) (\S+) \{(\S+)\} \((.+?)\)/;
\%foo
} <>])'
Вот ещё одно однострочное решение, без perl
:
apk list -I | cut -f1 -d' ' | sed -e 's/-r\d\+$//'| sed -e 's/\(.*\)-/\1 /'
Объяснение:
- Первый
sed
удаляет номер ревизии пакета (например,-r0
). - Второй
sed
заменяет последний дефис на пробел, ср. https://unix.stackexchange.com/questions/187889/how-do-i-replace-the-last-occurrence-of-a-character-in-a-string-using-sed.
Пример вывода:
% apk list -I | cut -f1 -d' ' | sed -e 's/-r\d\+$//'| sed 's/\(.*\)-/\1 /' | head
abuild 3.13.0
abuild-sudo 3.13.0
ack 3.7.0
agetty 2.40.1
agetty-openrc 0.54.2
alpine-base 3.21.0_alpha20240606
alpine-baselayout 3.6.5
alpine-baselayout-data 3.6.5
alpine-conf 3.18.1
alpine-keys 2.4
…которые вы можете легко разбирать, используя пробел в качестве разделителя.
Этот подход зависит только от sed, вызывает sed только один раз, не предполагает использование busybox sed (никакой зависимости от \d
) и может работать с версиями без pkgrel (есть такие на снимках OpenWRT).
apk list -I | sed -e 's/ .*//;s/-\(r[0-9]\+\)$/@@\1/;s/-\([^-]\+\)$/ \1/;s/@@/-/'
Объяснение:
- удаляет первый пробел и весь текст после него
- если
-rNUM
стоит в конце, превращает его в@@rNUM
- заменяет последний дефис на пробел
- заменяет
@@
обратно на-
Ответ или решение
Чтобы получить список установленных пакетов в Alpine Linux с версией в легко разбираемом формате, можно использовать регулярные выражения и инструменты для обработки текстовой информации. Основная проблема заключается в том, что стандартный вывод команды apk list -i
сложно парсить из-за наличия дефисов как в именах пакетов, так и в версиях.
Теория
Для надежного извлечения имен и версий пакетов можно использовать метод, который извлекает последние группы, разделенные дефисами, поскольку версия пакета и его ревизия всегда находятся в конце. Важно выделить части строки, которые соответствуют имени пакета, версии и ревизии.
Пример
Регулярное выражение, которое поможет извлечь все необходимые поля, выглядит следующим образом:
/(.+)-([^-]+)-r([^-]+) (\S+) \{(\S+)\} \((.+?)\)/
Это выражение разделяет строку на имя пакета, версию, ревизию, архитектуру, группу и лицензию. Используя Perl, можно преобразовать результат вывода в формат JSON, который легко парсить в различных языках программирования:
apk list -i | perl -MJSON -E 'print encode_json([map {
@foo{qw(pkgname pkgver pkgrel arch group license)} =
/(.+)-([^-]+)-r([^-]+) (\S+) \{(\S+)\} \((.+?)\)/;
\%foo
} <>])'
Применение
Если предпочтительнее использовать только стандартные инструменты командной строки, можно применить cut
и sed
для преобразования вывода в табличный формат. Этот подход не так мощен, как JSON, но позволяет легко разделять имя и версию пакета пробелом:
apk list -I | cut -f1 -d' ' | sed -e 's/-r\d\+$//'| sed -e 's/\(.*\)-/\1 /'
Порядок действий следующий:
cut
используется для удаления всех данных после первого пробела.- Первый
sed
удаляет номер ревизии пакета (например,-r0
). - Второй
sed
заменяет последний дефис на пробел, позволяя легко отделить имя пакета от его версии.
Таким образом, полученный вывод будет сочетаем с автоматическими инструментами, которые смогут легко обработать список пакетов, анализируя данные в табличном формате или переводя их в другие виды структурированных данных.