Перечислите пакеты верхнего уровня, установленные вручную, без их зависимостей

12

Есть много способов показать пакеты, установленные вручную apt, например:

apt-mark showmanual

Но иногда это слишком много. Например, если пользователь установил пакет вручную foo:

apt-get install foo

... и fooзависит от, barа bazзатем apt-mark showmanualбудет вывод:

bar
baz
foo

Как мы можем перечислить только пакеты верхнего уровня, установленные вручную ( то есть foo ) без их зависимостей ( то есть, нет baz, ни bar)?


Следующий код, кажется, работает, но вызов GNU несколько сотен раз слишком медленный (три часа с 4-ядерным процессором):parallelapt-rdepends

apt-mark showmanual | 
tee /tmp/foo | 
parallel "apt-rdepends -f Depends,PreDepends,Suggests,Recommends {} |
          tail +2" 2> /dev/null | 
tr -s ' ' '\n' | 
grep -v '[():]' | 
sort -Vu | 
grep -wv -f - /tmp/foo
АРУ
источник
Хм. Ответы и код OP, все такие разные, и возвращают несколько разные данные, поэтому я не совсем понимаю, какие данные метода являются наиболее правильными. Возможно, нужен ответ на опрос, начиная с минимальной тестовой системы и добавляя несколько программ за раз, чтобы увидеть, как и когда меняется результат.
АРУ

Ответы:

9

Это можно сделать с помощью API Python apt. Пакеты, в которых вы видите, apt-mark showmanualявляются именно теми, apt.cache.Cache()для которых is_installedверно и is_auto_installedложно. Но проще обработать зависимости:

#! /usr/bin/env python3

from apt import cache

manual = set(pkg for pkg in cache.Cache() if pkg.is_installed and not pkg.is_auto_installed)
depends = set(dep_pkg.name for pkg in manual for dep in pkg.installed.get_dependencies('PreDepends', 'Depends', 'Recommends') for dep_pkg in dep)

print('\n'.join(pkg.name for pkg in manual if pkg.name not in depends))

Даже в этом списке перечислены некоторые пакеты, которые я бы не ожидал увидеть там ( init, grep?!).

Мур
источник
На моей системе этот код выводит расширенный набор моего 3-часового кода, но не показывает никаких сюрпризов, как initи grep(может быть, ваши подходящие данные повреждены?), Также он показывает слишком много библиотек. OTOH, мой 3-часовой код пропускает несколько элементов, которые должны быть там, элементы, которые pythonпечатает вышеуказанный код. Возможно, отсутствующие элементы не были установлены с apt.
АРУ
@agc, наверное, потому что я не рекурсировал. Я попробую рекурсивный вариант после выходных. Однако даже с рекурсией я ожидал бы, что это будет намного быстрее, чем повторный вызов apt-rdepends
muru
Приведенный выше pythonкод работает в 3600 раз быстрее (т.е. это заняло 3 секунды), чем мой код (3 часа). Заглядывая вперед к тестированию рекурсивной версии ...
АРУ
3

Следующий сценарий оболочки ищет родителей всех установленных зависимостей.

function get_installed_packages() {
    apt list --installed | sed 's#/.*##'
}

function get_installed_packages_with_deps() {
    dpkg-query --show --showformat '${Package} ${Depends} \
        ${Pre-Depends}\n' $(get_installed_packages) | 
    sed 's/ ([^(]*)//g; s/:any\|,//g'
}

function get_package_relations() {
    awk '{print $1 " " $1; for(i = 2; i <= NF; i++) print $1 " " $i;}'
}

function add_marker() {
    echo "~ ~"
}

function resolve_parents() {
    tsort | sed -n '1,/~/ p' | head -n -1
}

(get_installed_packages_with_deps | get_package_relations; add_marker) | 
resolve_parents

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

Я заметил одну проблему с этим решением:
в графе зависимостей есть циклы. Эти записи игнорируются tsort.

sealor
источник
2

Вы можете найти все установленные вручную пакеты без их 1-го уровня зависимостей следующим образом:

apt-mark showmanual | sort > manually-installed.txt

apt show $(apt-mark showmanual) 2>/dev/null | 
grep -e ^Depends -e ^Pre-Depends > deps1.txt

cat deps1.txt | 
sed 's/^Depends: //; s/^Pre-Depends: //; 
     s/(.*)//g; s/:any//g' > deps2.txt

cat deps2.txt | tr -d ',|' | tr ' ' '\n' | grep -v ^$ |
sort -u > all-dep-packages.txt

grep -v -F -f all-dep-packages.txt manually-installed.txt

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

apt-mark showmanual | sort | grep -v -F -f <(apt show $(apt-mark showmanual) 2> /dev/null | grep -e ^Depends -e ^Pre-Depends | sed 's/^Depends: //; s/^Pre-Depends: //; s/(.*)//g; s/:any//g' | tr -d ',|' | tr ' ' '\n' | grep -v ^$ | sort -u)
sealor
источник
Намного быстрее. Это выводит то, что в основном является надмножеством кода OP, но также пропускает некоторые из них, такие как dasherпакет. В моей системе код OP поступает через sort -Vвыходы 475 линий, Мура в кодовых выходах 914 линий ( в том числе dasher), и выводит код этого ответа в 995 строк.
АРУ
Да, мой скрипт не учитывает полное дерево зависимостей. Вы можете попытаться адаптировать его для большего количества уровней иерархии.
герметик