Распечатать список установленных пакетов Alpine с указанием версии в легко парсируемом формате.

Вопрос или проблема

Я пытаюсь получить список установленных пакетов с версиями в легко обрабатываемом формате для автоматического анализа инструментом.

Я могу получить следующий вывод:

/ # 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 /'

Объяснение:

Пример вывода:

% 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 /'

Порядок действий следующий:

  1. cut используется для удаления всех данных после первого пробела.
  2. Первый sed удаляет номер ревизии пакета (например, -r0).
  3. Второй sed заменяет последний дефис на пробел, позволяя легко отделить имя пакета от его версии.

Таким образом, полученный вывод будет сочетаем с автоматическими инструментами, которые смогут легко обработать список пакетов, анализируя данные в табличном формате или переводя их в другие виды структурированных данных.

Оцените материал
Добавить комментарий

Капча загружается...