Überprüfen Sie, ob ein apt-get-Paket installiert ist, und installieren Sie es, wenn es nicht unter Linux ausgeführt wird

223

Ich arbeite an einem Ubuntu-System und derzeit mache ich Folgendes:

if ! which command > /dev/null; then
   echo -e "Command not found! Install? (y/n) \c"
   read
   if "$REPLY" = "y"; then
      sudo apt-get install command
   fi
fi

Würden die meisten Leute das tun? Oder gibt es eine elegantere Lösung?

John Jiang
quelle
7
Befehlsnamen spiegeln nicht immer den Paketnamen wider, zu dem sie gehören. Was ist dein größeres Ziel? Warum versuchen Sie nicht einfach, es zu installieren, und im schlimmsten Fall nicht, da es bereits installiert ist.
viam0Zah
8
Glücklicherweise ist apt-get install idempotent, so dass es sicher ist, es einfach auszuführen und sich keine Sorgen darüber zu machen, ob es installiert ist oder nicht.
David Baucum
@ DavidBaucums Kommentar sollte eine Antwort sein, die die meisten Stimmen erhalten würde.
Nirmal
@Nirmal, Antwort gemacht.
David Baucum
1
Verwandte, sollten Sie verwenden command -v <command>; nicht which <command>. Siehe auch Überprüfen, ob ein Programm aus einem Bash-Skript vorhanden ist .
JWW

Antworten:

314

Geben Sie Folgendes ein, um zu überprüfen, ob packagenameinstalliert wurde:

dpkg -s <packagename>

Sie können auch dpkg-queryeine übersichtlichere Ausgabe für Ihren Zweck verwenden und auch Platzhalter akzeptieren.

dpkg-query -l <packagename>

Um herauszufinden, welches Paket das besitzt command, versuchen Sie:

dpkg -S `which <command>`

Weitere Details finden Sie im Artikel Finden Sie heraus, ob das Paket unter Linux und im dpkg-Spickzettel installiert ist .

viam0Zah
quelle
32
Wenn Sie als Person dies NICHT programmgesteuert wünschen, können Sie diese Informationen in ihrer jetzigen Form verwenden. Sie können sich hier jedoch nicht einfach auf Rückkehrcodes für die Skripterstellung oder die Ausgabe / fehlende Ausgabe allein für die Skripterstellung verlassen. Sie müssten die Ausgabe dieser Befehle scannen, um ihre Nützlichkeit für diese Frage einzuschränken.
UpAndAdam
4
Seltsamerweise habe ich kürzlich entdeckt, dass die dpkg-Abfrage verwendet wurde, um 1 für ein fehlendes Paket zurückzugeben. Jetzt (Ubuntu 12.04) wird 0 zurückgegeben, was alle möglichen Probleme mit meinem Jenkins-Build-Node-Setup-Skript verursacht! dpkg -s gibt 0 für das installierte Paket und 1 für das nicht installierte Paket zurück.
Therealstubot
18
Hey, OP hat um ifVerwendung gebeten . Ich suche auch nach ifVerwendung.
Tomáš Zato - Wiedereinsetzung Monica
1
@Therealstubot: Ich verwende auch Ubuntu 12.04 und gebe dpkg -sbei fehlenden Paketen 1 und ansonsten 0 zurück, wie es sollte. Wie war der Unterschied zu früheren (oder neueren) Versionen?
MestreLion
4
Ein Hinweis: dpkg -sGibt Null zurück, wenn ein Paket installiert und dann entfernt wurde - in diesem Fall ist es Status: deinstall ok config-filesoder ähnlich, also "ok" - für mich ist dies also kein sicherer Test. dpkg-query -lscheint auch in diesem Fall kein nützliches Ergebnis zu liefern.
scharf
86

Um etwas genauer zu sein, hier ein kleines Bash-Skript, das nach einem Paket sucht und es bei Bedarf installiert. Natürlich können Sie auch andere Dinge tun, wenn Sie feststellen, dass das Paket fehlt, z. B. einfach mit einem Fehlercode beenden.

REQUIRED_PKG="some-package"
PKG_OK=$(dpkg-query -W --showformat='${Status}\n' $REQUIRED_PKG|grep "install ok installed")
echo Checking for $REQUIRED_PKG: $PKG_OK
if [ "" = "$PKG_OK" ]; then
  echo "No $REQUIRED_PKG. Setting up $REQUIRED_PKG."
  sudo apt-get --yes install $REQUIRED_PKG 
fi

Wenn das Skript in einer GUI ausgeführt wird (z. B. ein Nautilus-Skript), möchten Sie wahrscheinlich den Aufruf "sudo" durch einen Aufruf "gksudo" ersetzen.

Urhixidur
quelle
5
--force-yesscheint eine schlechte Idee. Aus der Manpage: "Dies ist eine gefährliche Option, die dazu führt, dass apt-get ohne Aufforderung fortgesetzt wird, wenn es etwas potenziell Schädliches tut. Es sollte nur in ganz besonderen Situationen verwendet werden. Die Verwendung von --force-yes kann Ihr System möglicherweise zerstören ! " Die Verwendung in einem Skript macht es noch schlimmer.
Reduzierung der Aktivität
68

Dieser Einzeiler gibt 1 (installiert) oder 0 (nicht installiert) für das 'Nano'-Paket zurück.

$(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed")

auch wenn das Paket nicht existiert / nicht verfügbar ist.

Im folgenden Beispiel wird das 'nano'-Paket installiert, wenn es nicht installiert ist.

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
Seb
quelle
4
Meine Variation dazu:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner
@ThorSummoner: Möchtest du erklären, warum deine besser ist?
Knocte
1
@knocte Ich bin mir nicht sicher, ob es ein Argument dafür gibt, objektiv besser zu sein. Obwohl ich zuversichtlich bin, dass der Einzeiler des wörtlichen Posts die Ergebnisausgabe ausführen wird, möchte ich nicht in einer Antwort baumeln lassen. Der eine Liner, den ich zeige, zeigt beispielhaft, wie man nur den Exit-Code erhält (druckt).
ThorSummoner
1
@ThorSummoner Sie brauchen keinen grep -Peinfachen regulären Ausdruck wie diesen.
Tripleee
7
Einfacher: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Keine Notwendigkeit zu verwenden grep -c, verwenden Sie einfach den Exit-Status vongrep
Stephen Ostermiller
17

dpkg -s programmatische Nutzung mit automatischer Installation

Ich mag dpkg -ses, 1wenn es mit dem Status beendet wird, wenn eines der Pakete nicht installiert ist, was die Automatisierung vereinfacht:

pkgs='qemu-user pandoc'
if ! dpkg -s $pkgs >/dev/null 2>&1; then
  sudo apt-get install $pkgs
fi

man dpkg dokumentiert den Exit-Status leider nicht, aber ich denke, es sollte einigermaßen sicher sein, sich darauf zu verlassen:

-s, --status package-name...
    Report status of specified package.

Eine Sache zu beachten ist, dass Laufen:

sudo apt remove <package-name>

entfernt nicht unbedingt alle Dateien sofort für einige Pakete (aber für andere, nicht sicher warum?) und markiert nur das Paket zum Entfernen.

In diesem Zustand scheint das Paket weiterhin verwendbar zu sein und da seine Dateien noch vorhanden sind, wird es jedoch zum späteren Entfernen markiert.

Zum Beispiel, wenn Sie ausführen:

pkg=certbot

sudo apt install -y "$pkg"
dpkg -s "$pkg"
echo $?

sudo apt remove -y "$pkg"
dpkg -s "$pkg"
echo $?
ls -l /usr/lib/python3/dist-packages/certbot/reporter.py

sudo apt remove --purge certbot
dpkg -s "$pkg"
echo $?
ls -l /usr/lib/python3/dist-packages/certbot/reporter.py

dann:

  • die ersten beiden echo $?geben aus 0, nur die dritte gibt aus1

  • Die Ausgabe für die erste dpkg -s certbotenthält:

    Status: deinstall ok installed

    während der zweite sagt:

    Status: deinstall ok config-files

    und es verschwindet erst nach der Säuberung:

    dpkg-query: package 'certbot' is not installed and no information is available
  • Die Datei /etc/logrotate.d/certbotist nach apt remove, aber nicht nach dem System noch vorhanden --purge.

    Die Datei /usr/lib/python3/dist-packages/certbot/reporter.pyist jedoch auch danach noch vorhanden --purge.

Ich verstehe nicht warum, aber mit dem helloPaket zeigt das zweite dpkgnach apt remove, dass das Paket bereits entfernt wurde ohne --purge:

dpkg-query: package 'hello' is not installed and no information is available

Die Dokumentationen sind auch sehr unklar, zB:

sudo apt dselect-upgrade

wurde nicht entfernt, certbotals es als markiert wurde deinstall, obwohl dies man apt-getdarauf hinzudeuten scheint:

dselect-upgradewird in Verbindung mit dem traditionellen Debian-Verpackungs-Frontend dselect (1) verwendet. dselect-upgrade folgt den Änderungen, die dselect (1) im Feld Status der verfügbaren Pakete vorgenommen hat, und führt die zur Realisierung dieses Status erforderlichen Aktionen aus (z. B. Entfernen alter und Installation neuer Pakete).

Siehe auch:

Getestet unter Ubuntu 19.10.

Python- aptPaket

aptIn Ubuntu 18.04 gibt es ein vorinstalliertes Python 3-Paket , das eine Python-Apt-Oberfläche verfügbar macht!

Ein Skript, das prüft, ob ein Paket installiert ist, und es installiert, wenn nicht, finden Sie unter: So installieren Sie ein Paket mithilfe der Python-Apt-API

Hier ist eine Kopie als Referenz:

#!/usr/bin/env python
# aptinstall.py

import apt
import sys

pkg_name = "libjs-yui-doc"

cache = apt.cache.Cache()
cache.update()
cache.open()

pkg = cache[pkg_name]
if pkg.is_installed:
    print "{pkg_name} already installed".format(pkg_name=pkg_name)
else:
    pkg.mark_install()

    try:
        cache.commit()
    except Exception, arg:
        print >> sys.stderr, "Sorry, package installation failed [{err}]".format(err=str(arg))

Überprüfen Sie PATHstattdessen, ob eine ausführbare Datei vorhanden ist

Siehe: Wie kann ich anhand eines Bash-Skripts überprüfen, ob ein Programm vorhanden ist?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
Ciro, Sie können sich nicht auf den Exit-Code "dpkg -s" verlassen. Wenn Sie beispielsweise ein Paket "apt installiert" haben, es dann "apt entfernen" und versucht haben, "dpkg -s packagename" zu verwenden, werden Sie den Status "deinstallieren" und den Code null beenden (als ob installiert). Sie müssen die Ausgabe "dpkg -s" bro analysieren.
Dmitry Shevkoplyas
@DmitryShevkoplyas danke für den Bericht. Ich konnte unter Ubuntu 19.10 nicht reproduzieren mit : sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Können Sie weitere Details angeben?
Ciro Santilli 29 冠状 病 六四 事件 29
1
in der Tat - für Ihren Testfall mit "Hallo" -Paket zeigt "dpkg -s" das Paket korrekt als nicht installiert an und gibt den erwarteten Exit-Code "1" an. Aber versuchen Sie es mit der gleichen Installation / Entfernen-Prüfung mit dem Paket "certbot". Dann wird "Status: deinstall ok config-files" angezeigt, wie die Ausgabe von "dpkg -s" nach Ihrem "apt remove certbot" und dem Exit-Code uns falsch anzeigt "0". Meine falsche Annahme war, dass es für jedes andere Paket genau der Fall ist, aber es scheint, dass es nicht für alle gleich ist, was noch schlimmer und weniger vorhersehbar ist. Analysiere "dpkg -s" du musst (c) Yoda :)
Dmitry Shevkoplyas
11

Ich biete dieses Update an, da Ubuntu sein "Personal Package Archive" (PPA) hinzugefügt hat, als diese Frage beantwortet wurde, und PPA-Pakete ein anderes Ergebnis haben.

  1. Native Debian-Repository-Paket nicht installiert:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Auf dem Host registriertes und installiertes PPA-Paket:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Auf dem Host registriertes, aber nicht installiertes PPA-Paket:

    ~$ dpkg-query -l domy-ce
    ~$ echo $?
    0
    ~$ sudo apt-get remove domy-ce
    [sudo] password for user: 
    Reading package lists... Done
    Building dependency tree       
    Reading state information... Done
    Package domy-ce is not installed, so not removed
    0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Ebenfalls veröffentlicht unter: /superuser/427318/test-if-a-package-is-installed-in-apt/427898

tahoar
quelle
2
Wenn Sie ein Paket installieren und entfernen, verwenden Sie als Nächstes das Paket dpkg-query. echo $? wird auch dann 0 sein, wenn das Paket nicht installiert ist.
Pol Hallen
8

UpAndAdam schrieb:

Sie können sich hier jedoch nicht einfach auf Rückkehrcodes für die Skripterstellung verlassen

In meiner Erfahrung , die Sie können auf dkpg die verlassen Exit - Codes.

Der Rückkehrcode von dpkg -s ist 0, wenn das Paket installiert ist, und 1, wenn dies nicht der Fall ist. Die einfachste Lösung, die ich gefunden habe, war:

dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>

Funktioniert gut für mich ...

rocka84
quelle
11
Danach apt-get remove <package>wird dpkg -s <package>immer noch 0 zurückgegeben, obwohl das Paketdeinstalled
ThorSummoner
7

Das scheint ziemlich gut zu funktionieren.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Es wird entweder zurückgegeben, 0wenn es nicht installiert ist, oder eine Nummer, > 0wenn es installiert ist.
Sandmann
quelle
8
grep | wc -list ein Antimuster. Um zu überprüfen, ob etwas vorhanden ist, möchten Sie einfach grep -q. Verwenden Sie diese Option, um Vorkommen tatsächlich zu zählen (was in solchen Szenarien selten nützlich ist) grep -c.
Tripleee
@tripleee Also dpkg -s zip | grep -c "Package: zip"? (mit zip als Beispielpaket)
David Tabernero M.
@Davdriver Das ist nicht genau das, was oben tut, aber ja. In einem Skript möchten Sie wahrscheinlich grep -q 'Package: zip'einen Exit-Code zurückgeben, der angibt, ob das Ergebnis gefunden wurde, ohne etwas zu drucken.
Tripleee
Dies scheint auch für deinstallierte Pakete gut zu
funktionieren
4

Ich habe mich aufgrund der Antwort von Nultyi für eine entschieden :

MISSING=$(dpkg --get-selections $PACKAGES 2>&1 | grep -v 'install$' | awk '{ print $6 }')
# Optional check here to skip bothering with apt-get if $MISSING is empty
sudo apt-get install $MISSING

Grundsätzlich ist die Fehlermeldung von dpkg --get-selections viel einfacher zu analysieren als die meisten anderen, da sie keine Status wie "deinstallieren" enthält. Es können auch mehrere Pakete gleichzeitig überprüft werden, was nicht nur mit Fehlercodes möglich ist.

Erklärung / Beispiel:

$ dpkg --get-selections  python3-venv python3-dev screen build-essential jq
dpkg: no packages found matching python3-venv
dpkg: no packages found matching python3-dev
screen                                          install
build-essential                                 install
dpkg: no packages found matching jq

Daher entfernt grep installierte Pakete aus der Liste, und awk zieht die Paketnamen aus der Fehlermeldung heraus, was dazu führt MISSING='python3-venv python3-dev jq', dass sie trivial in einen Installationsbefehl eingefügt werden können.

Ich gebe nicht blind eine aus, apt-get install $PACKAGESda dies, wie in den Kommentaren erwähnt, unerwartet Pakete aktualisieren kann, die Sie nicht geplant haben. Keine gute Idee für automatisierte Prozesse, von denen erwartet wird, dass sie stabil sind.

Izkata
quelle
Ich mag diese Lösung. Prägnant und Tests für mehrere Pakete gleichzeitig. Sie können die optionale Prüfung auch so einfach wie[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk
3

Ich habe festgestellt, dass alle oben genannten Lösungen ein falsches Positiv erzeugen können, wenn ein Paket installiert und dann entfernt wird, das Installationspaket jedoch auf dem System verbleibt.

So replizieren Sie: Paket installieren Paket apt-get install curl
entfernenapt-get remove curl

Testen Sie nun die obigen Antworten.

Der folgende Befehl scheint diese Bedingung zu lösen:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Dies führt zu einer endgültigen Installation oder Nichtinstallation

Kennzeichen
quelle
Leider nicht ganz - andere mögliche Ergebnisse in diesem Fall sind config-files- daher denke ich, dass ein Finale | grep -q "installed"wirklich erforderlich ist, um einen funktionalen Exit-Code-Status zu erhalten.
scharf
machen Sie das| grep -q '^installed$'
scharf
3

Es scheint, dass es heutzutage apt-geteine Option gibt --no-upgrade, die genau das tut, was das OP will:

--no-upgradeAktualisieren Sie keine Pakete. In Verbindung mit der Installation verhindert No-Upgrade, dass aufgelistete Pakete aktualisiert werden, wenn sie bereits installiert sind.

Manpage von https://linux.die.net/man/8/apt-get

Daher können Sie verwenden

apt-get install --no-upgrade package

und packagewird nur installiert, wenn es nicht ist.

Giovanni Mascellani
quelle
2

Das wird es tun. apt-get installist idempotent.

sudo apt-get install command
David Baucum
quelle
5
Es gibt Szenarien, in denen das Ausführen apt-get installeines Pakets unerwünscht ist, wenn das Paket bereits installiert ist, obwohl der Befehl selbst idempotent ist. In meinem Fall installiere ich ein Paket auf einem Remote-System mit Ansibles Raw-Modul, das das System jedes Mal als geändert meldet, wenn ich apt-get installwahllos ausgeführt werde. Eine Bedingung löst dieses Problem.
JBentley
1
@JBentley Das ist ein guter Punkt. Pakete, die als Teil einer Abhängigkeit installiert werden, werden als manuell installiert markiert und dann nicht entfernt, wenn die Abhängigkeit entfernt wird, wenn Sie sie installieren.
David Baucum
2

Verwenden:

apt-cache policy <package_name>

Wenn es nicht installiert ist, wird Folgendes angezeigt:

Installed: none

Andernfalls wird Folgendes angezeigt:

Installed: version
Mohammed Noureldin
quelle
1

Diese Funktion ist bereits in Ubuntu und Debian im command-not-foundPaket enthalten.

camh
quelle
15
matt @ matt-ubuntu: ~ $ Befehl nicht gefunden Befehl nicht gefunden: Befehl nicht gefunden ... lol.
Matt Fletcher
1
command-not-foundist ein interaktiver Helfer und kein Tool, um sicherzustellen, dass Sie die gewünschten Abhängigkeiten haben. Der richtige Weg, Abhängigkeiten zu deklarieren, besteht natürlich darin, Ihre Software in ein Debian-Paket zu packen und die Depends:Deklaration in der debian/controlDatei des Pakets ordnungsgemäß auszufüllen .
Tripleee
1
apt list [packagename]

scheint der einfachste Weg zu sein, dies außerhalb von dpkg und älteren apt- * Tools zu tun

Erich
quelle
Es ist gut für die manuelle Überprüfung geeignet, gibt jedoch eine Warnung aus, die besagt, dass apt nicht für Skripte vorgesehen ist - im Gegensatz zu den apt- * -Tools.
Hontvári Levente
1

Ich hatte eine ähnliche Anforderung, als ich den Test lokal anstatt im Docker ausführte. Grundsätzlich wollte ich nur gefundene .deb-Dateien installieren, die nicht bereits installiert waren.

# If there are .deb files in the folder, then install them
if [ `ls -1 *.deb 2> /dev/null | wc -l` -gt 0 ]; then
  for file in *.deb; do
    # Only install if not already installed (non-zero exit code)
    dpkg -I ${file} | grep Package: | sed -r 's/ Package:\s+(.*)/\1/g' | xargs dpkg -s
    if [ $? != 0 ]; then
        dpkg -i ${file}
    fi;
  done;
else
  err "No .deb files found in '$PWD'"
fi

Ich denke, das einzige Problem, das ich sehen kann, ist, dass die Versionsnummer des Pakets nicht überprüft wird. Wenn es sich also bei der .deb-Datei um eine neuere Version handelt, wird das aktuell installierte Paket nicht überschrieben.

Craig
quelle
1

Für Ubuntu bietet apt eine recht anständige Möglichkeit, dies zu tun. Unten finden Sie ein Beispiel für Google Chrome:

apt -qq list google-chrome-stable 2>/dev/null | grep -qE "(installed|upgradeable)" || apt-get install google-chrome-stable

Ich leite die Fehlerausgabe auf null um, weil apt vor der Verwendung seiner "instabilen CLI" warnt. Ich vermute, dass das Listenpaket stabil ist, daher denke ich, dass es in Ordnung ist, diese Warnung wegzuwerfen. Das -qq macht apt super leise.

carlin.scott
quelle
1
Dies wird nicht richtig funktionieren, wenn etwas "aktualisierbar" ist
Pawel Barcik
@ PawelBarcik guter Punkt. Ich habe die Antwort aktualisiert, um mit dieser Situation umzugehen.
carlin.scott
0

Dieser Befehl ist der denkwürdigste:

dpkg --get-selections <package-name>

Wenn es installiert ist, wird Folgendes gedruckt:

<Paketname> installieren

Andernfalls wird gedruckt

Es wurden keine Pakete gefunden, die mit <Paketname> übereinstimmen.

Dies wurde unter Ubuntu 12.04.1 (Precise Pangolin) getestet.

iNulty
quelle
4
dpkg --get-selections <package-name>setzt den Exit-Code nicht auf ungleich Null, wenn das Paket nicht gefunden wird.
Lucas
0

Viele Dinge wurden erzählt, aber für mich ist der einfachste Weg:

dpkg -l | grep packagename
freedev
quelle
0

In Bash:

PKG="emacs"
dpkg-query -l $PKG > /dev/null || sudo apt install $PKG

Beachten Sie, dass Sie in PKG eine Zeichenfolge mit mehreren Paketen haben können.

Daruma
quelle
0

Ich benutze folgenden Weg:

which mySQL 2>&1|tee 1> /dev/null
  if [[ "$?" == 0 ]]; then
                echo -e "\e[42m MySQL already installed. Moving on...\e[0m"
        else
        sudo apt-get install -y mysql-server
                if [[ "$?" == 0 ]]; then
                        echo -e "\e[42mMy SQL installed\e[0m"
                else
                        echo -e "\e[42Installation failed\e[0m"
                fi
        fi
Nitish Jadia
quelle