Répertorier les packages installés manuellement de niveau supérieur sans leurs dépendances

12

Il existe de nombreuses façons d'afficher les packages installés manuellement à l'aide de apt, tels que:

apt-mark showmanual

Mais parfois, cette sortie est trop. Par exemple, si l'utilisateur a installé manuellement le package foo:

apt-get install foo

... et foodépendait baret baz, puis apt-mark showmanualaffichera:

bar
baz
foo

Comment pouvons-nous répertorier uniquement les packages de niveau supérieur installés manuellement ( c'est-à-dire foo ) sans leurs dépendances ( c'est-à-dire non baz, ni bar)?


Le code suivant semble fonctionner, mais appeler GNU quelques centaines de fois est trop lent (trois heures avec un processeur à 4 cœurs):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
agc
la source
Hmm. Les réponses et le code OP sont tous si différents et renvoient des données quelque peu différentes, que je deviens un peu flou sur les données de la méthode qui sont les plus correctes. Peut-être qu'une réponse à l'enquête est nécessaire, à partir d'un système de test minimal et en ajoutant quelques programmes à la fois pour voir comment et quand la sortie varie.
agc

Réponses:

9

Cela pourrait être fait en utilisant l'API Python apt. Les packages que vous voyez apt-mark showmanualsont exactement ceux apt.cache.Cache()pour lesquels is_installedest vrai et is_auto_installedfaux. Mais, il est plus facile de traiter les dépendances:

#! /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))

Même cela répertorie certains packages que je ne m'attendrais pas à voir là-bas ( init, grep?!).

muru
la source
Sur mon système, ce code génère un sur-ensemble de mon code de 3 heures, mais ne montre aucune surprise comme initet grep, (peut-être que vos données apt sont corrompues?), Il montre également trop de bibliothèques. OTOH, mon code de 3 heures manque quelques éléments qui devraient être là, des éléments que le pythoncode ci-dessus imprime. Il est possible que les éléments manquants n'aient pas été installés avec apt.
agc
@agc c'est probablement parce que je n'ai pas récusé. Je vais essayer une option récursive après le week-end. Même avec la récursivité, cependant, je m'attends à ce que ce soit beaucoup plus rapide que d'appeler apt-rdepends à plusieurs reprises
muru
Le pythoncode ci-dessus est 3600 fois plus rapide (c'est-à-dire qu'il a fallu 3 secondes) que mon code (3 heures). Au plaisir de tester la version récursive ...
agc
3

Le script shell suivant recherche les parents de toutes les dépendances installées.

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

J'ai utilisé tsortdans ce script. Je suppose que lors de l'ajout d'un marqueur à la fin sans dépendances, le marqueur sera également la dernière entrée sans dépendances dans mon résultat. Je peux donc faire la différence entre le dernier paquet sans dépendances et le premier paquet avec dépendances.

J'ai remarqué un problème avec cette solution:
il y a des cycles dans le graphique des dépendances. Ces entrées sont ignorées par tsort.

scelleur
la source
2

Vous pouvez trouver tous les packages installés manuellement sans leur 1er niveau de dépendances comme suit:

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

Vous pouvez également utiliser la magie à une ligne suivante:

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)
scelleur
la source
Plus vite. Cela génère ce qui est principalement un surensemble du code OP, mais il en manque également quelques-uns, comme le dasherpackage. Sur mon système , le code OP redirigée vers les sort -Vsorties 475 lignes, de Muru code de 914 lignes, (y compris dasher), et les sorties de code de cette réponse 995 lignes.
agc
Oui, mon script ne considère pas l'arborescence de dépendance complète. Vous pouvez essayer de l'adapter à plusieurs niveaux de hiérarchie.
sealor