Listen Sie manuell installierte Pakete der obersten Ebene ohne ihre Abhängigkeiten auf

11

Es gibt viele Möglichkeiten, manuell installierte Pakete anzuzeigen apt, z. B.:

apt-mark showmanual

Aber manchmal ist diese Ausgabe zu viel. Zum Beispiel, wenn der Benutzer das Paket manuell installiert hat foo:

apt-get install foo

... und foohing davon ab barund würde bazdann apt-mark showmanualausgeben:

bar
baz
foo

Wie können wir nur die manuell installierten Pakete der obersten Ebene ( dh foo ) ohne ihre Abhängigkeiten ( dh nicht bazoder noch bar) auflisten?


Der folgende Code scheint zu funktionieren, aber ein paar hundert Mal GNU- parallel Aufrufe apt-rdependssind zu langsam (drei Stunden mit einer 4-Kern-CPU):

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
quelle
Hmm. Die Antworten und der OP-Code sind alle so unterschiedlich und geben etwas unterschiedliche Daten zurück, dass ich ein bisschen unscharf werde, welche Daten der Methode am korrektesten sind. Möglicherweise ist eine Umfrageantwort erforderlich, die von einem minimalen Testsystem ausgeht und jeweils einige Programme hinzufügt, um festzustellen, wie und wann die Ausgabe variiert.
Agc

Antworten:

9

Dies kann mithilfe der Python apt-API erfolgen. Die Pakete apt-mark showmanual, in apt.cache.Cache()denen Sie sehen, sind genau diejenigen, für die is_installedwahr und is_auto_installedfalsch ist. Es ist jedoch einfacher, die Abhängigkeiten zu verarbeiten:

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

Auch hier sind einige Pakete aufgeführt, die ich dort nicht erwarten würde ( init, grep?!).

muru
quelle
Auf meinem System gibt dieser Code eine Obermenge meines 3-Stunden-Codes aus, zeigt jedoch keine Überraschungen wie initund grep(möglicherweise sind Ihre passenden Daten beschädigt?) Auch zu viele Bibliotheken. OTOH, in meinem 3-Stunden-Code fehlen einige Elemente, die vorhanden sein sollten, Elemente, die mit dem obigen pythonCode gedruckt werden. Möglicherweise wurden die fehlenden Elemente nicht mit installiert apt.
Agc
@agc das liegt wahrscheinlich daran, dass ich nicht rekursiv war. Ich werde nach dem Wochenende eine rekursive Option versuchen. Selbst mit Rekursion würde ich erwarten, dass dies viel schneller ist als das wiederholte Aufrufen von apt-rdepends
muru
Der obige pythonCode ist 3600-mal schneller (dh es dauerte 3 Sekunden) als mein Code (3 Stunden). Ich freue mich darauf, die rekursive Version zu testen ...
agc
3

Das folgende Shell-Skript sucht nach den übergeordneten Elementen aller installierten Abhängigkeiten.

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

Ich habe tsortin diesem Skript verwendet. Ich gehe davon aus, dass beim Hinzufügen eines Markers am Ende ohne Abhängigkeiten der Marker auch der letzte Eintrag ohne Abhängigkeiten in meinem Ergebnis ist. So kann ich zwischen dem letzten Paket ohne Abhängigkeiten und dem ersten Paket mit Abhängigkeiten unterscheiden.

Bei dieser Lösung ist mir ein Problem aufgefallen:
Das Abhängigkeitsdiagramm enthält Zyklen. Diese Einträge werden von ignoriert tsort.

Versiegelung
quelle
2

Sie können alle manuell installierten Pakete ohne ihre erste Abhängigkeitsebene wie folgt finden:

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

Sie können auch die folgende Einzeiler-Magie verwenden:

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)
Versiegelung
quelle
Viel schneller. Dies gibt aus, was meistens eine Obermenge des OP-Codes ist, aber es fehlen auch einige, wie das dasherPaket. Auf meinem System verrohrt der OP - Code durch sort -VAusgänge 475 Zeilen, muru der Code - Ausgänge 914 Zeilen (einschließlich dasher), und dieser Code gibt die Antwort 995 Zeilen.
Agc
Ja, mein Skript berücksichtigt nicht den vollständigen Abhängigkeitsbaum. Sie könnten versuchen, es für mehr Hierarchieebenen anzupassen.
Sealor