Sprawdź, czy pakiet apt-get jest zainstalowany, a następnie zainstaluj go, jeśli nie jest on w systemie Linux

223

Pracuję na systemie Ubuntu i obecnie robię to:

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

Czy to zrobiłoby większość ludzi? A może istnieje bardziej eleganckie rozwiązanie?

John Jiang
źródło
7
Nazwy poleceń nie zawsze odzwierciedlają nazwę pakietu, do którego należą. Jaki jest twój większy cel? Dlaczego po prostu nie spróbujesz go zainstalować, aw najgorszym przypadku nie, ponieważ jest już zainstalowany.
viam0Zah
8
Na szczęście apt-get install jest idempotentny, więc po prostu uruchom go i nie martw się, czy zostanie zainstalowany, czy nie.
David Baucum
Komentarz @ DavidBaucum powinien być odpowiedzią, która uzyska największą liczbę głosów.
Nirmal,
@Nirmal, odpowiedź udzielona.
David Baucum,
1
Powiązane, powinieneś użyć command -v <command>; Nie which <command>. Zobacz także Sprawdź, czy program istnieje ze skryptu Bash .
jww

Odpowiedzi:

314

Aby sprawdzić, czy packagenamezostał zainstalowany, wpisz:

dpkg -s <packagename>

Możesz również użyć dpkg-querytego, który ma ładniejszy wynik dla twojego celu i akceptuje również dzikie karty.

dpkg-query -l <packagename>

Aby dowiedzieć się, który pakiet jest właścicielem command, spróbuj:

dpkg -S `which <command>`

Aby uzyskać więcej informacji, zobacz artykuł Sprawdź, czy pakiet jest zainstalowany w systemie Linux i ściągawkę dpkg .

viam0Zah
źródło
31
Jeśli jako osoba chcesz tego NIEprogramowo, możesz użyć tej informacji w jej obecnym kształcie. Jednak nie można po prostu polegać na kodach powrotu w przypadku skryptów lub na samym wyjściu / braku danych wyjściowych do skryptowania. Będziesz musiał zeskanować dane wyjściowe tych poleceń, ograniczając ich przydatność do tego pytania.
UpAndAdam
4
Co dziwne, niedawno odkryłem, że dpkg-query zwracało 1 w brakującym pakiecie, teraz (Ubuntu 12.04) zwraca 0, powodując różnego rodzaju problemy w moim skrypcie instalacyjnym węzła jenkins! dpkg -s zwraca 0 dla pakietu zainstalowanego, a 1 dla pakietu niezainstalowanego.
Therealstubot
17
Hej, OP poprosił o ifużycie. Szukam również ifzastosowania.
Tomáš Zato - Przywróć Monikę
1
@Therealstubot: Używam również Ubuntu 12.04 i dpkg -szwraca 1 w przypadku brakujących pakietów i 0 w przeciwnym razie, tak jak powinno. Czym różniły się wcześniejsze (lub najnowsze) wersje?
MestreLion,
4
uwaga: dpkg -szwraca zero, jeśli pakiet został zainstalowany, a następnie usunięty - w takim przypadku jest Status: deinstall ok config-filespodobny lub podobny, więc jest „ok” - więc dla mnie nie jest to bezpieczny test. dpkg-query -lw tym przypadku nie wydaje się też przydatny wynik.
chętny
86

Aby być bardziej precyzyjnym, oto skrypt bash, który sprawdza pakiet i instaluje go w razie potrzeby. Oczywiście możesz zrobić inne rzeczy po stwierdzeniu braku pakietu, na przykład po prostu wyjść z kodem błędu.

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

Jeśli skrypt działa w GUI (np. Jest to skrypt Nautilus), prawdopodobnie będziesz chciał zastąpić wywołanie „sudo” wywołaniem „gksudo”.

Urhixidur
źródło
5
--force-yeswydaje się kiepskim pomysłem. Ze strony podręcznika man: „Jest to niebezpieczna opcja, która spowoduje, że apt-get będzie kontynuował bez pytania, czy robi coś potencjalnie szkodliwego. Nie należy go używać z wyjątkiem bardzo szczególnych sytuacji. Użycie --force-yes może potencjalnie zniszczyć twój system ! ” Używanie go w skrypcie czyni go jeszcze gorszym.
ograniczenie aktywności
68

Ta linijka zwraca 1 (zainstalowany) lub 0 (nie zainstalowany) dla pakietu „nano”.

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

nawet jeśli pakiet nie istnieje / nie jest dostępny.

Poniższy przykład instaluje pakiet „nano”, jeśli nie jest zainstalowany.

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
Seb
źródło
4
Moja odmiana w tej sprawie:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner,
@ThorSummoner: staraj się wyjaśnić, dlaczego twój jest lepszy?
knocte
1
@knocte Nie jestem pewien, czy można argumentować o byciu obiektywnie lepszym. Chociaż jestem przekonany, że linijka dosłownego postu wykona wynik, czego nie chciałbym zostawić wisi w odpowiedzi. Jedna linijka, którą pokazuję, stanowi przykład (wydruk) tylko kodu wyjścia.
ThorSummoner
1
@ThorSummoner Nie potrzebujesz grep -Ptakiego prostego wyrażenia regularnego.
tripleee
7
Prostsze: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Nie trzeba używać grep -c, wystarczy użyć statusu wyjściagrep
Stephen Ostermiller
17

dpkg -s programowe użycie z automatyczną instalacją

Podoba mi się, dpkg -sgdy kończy pracę ze statusem, 1jeśli którykolwiek z pakietów nie jest zainstalowany, co ułatwia automatyzację:

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

man dpkg nie dokumentuje niestety statusu wyjścia, ale myślę, że należy na nim polegać:

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

Należy zauważyć, że bieganie:

sudo apt remove <package-name>

niekoniecznie natychmiast usuwa wszystkie pliki dla niektórych pakietów (ale robi to dla innych, nie wiesz dlaczego?) i po prostu zaznacza pakiet do usunięcia.

W tym stanie pakiet wydaje się być nadal użyteczny, a ponieważ jego pliki są nadal obecne, ale jest oznaczony do późniejszego usunięcia.

Na przykład, jeśli uruchomisz:

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

następnie:

  • pierwsze dwa echo $?wyjścia 0, tylko trzeci1

  • wynik pierwszego dpkg -s certbotzawiera:

    Status: deinstall ok installed

    podczas gdy drugi mówi:

    Status: deinstall ok config-files

    i znika dopiero po wyczyszczeniu:

    dpkg-query: package 'certbot' is not installed and no information is available
  • plik /etc/logrotate.d/certbotjest nadal obecny w systemie po apt remove, ale nie później --purge.

    Jednak plik /usr/lib/python3/dist-packages/certbot/reporter.pyjest nadal obecny, nawet po --purge.

Nie rozumiem dlaczego, ale z hellopakietem drugi dpkgpo apt removepokazuje, że pakiet został już usunięty bez --purge:

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

Dokumentacje są również bardzo niejasne, np .:

sudo apt dselect-upgrade

nie usunął, certbotgdy został oznaczony jako deinstall, mimo że man apt-getwydaje się wskazywać, że:

dselect-upgradejest używany w połączeniu z tradycyjnym interfejsem Debiana, dselect (1). dselect-upgrade śledzi zmiany wprowadzone przez dselect (1) w polu Status dostępnych pakietów i wykonuje działania niezbędne do realizacji tego stanu (na przykład usunięcie starych i instalacja nowych pakietów).

Zobacz też:

Testowane na Ubuntu 19.10.

aptPakiet Python

W aptUbuntu 18.04 znajduje się preinstalowany pakiet Python 3, który udostępnia interfejs apt Python!

Skrypt sprawdzający, czy pakiet jest zainstalowany i instaluje go, jeśli nie, można go zobaczyć pod adresem: Jak zainstalować pakiet za pomocą interfejsu API python-apt

Oto kopia w celach informacyjnych:

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

Sprawdź, czy PATHzamiast niego znajduje się plik wykonywalny

Zobacz: Jak mogę sprawdzić, czy program istnieje ze skryptu Bash?

Ciro Santilli
źródło
Ciro, nie możesz polegać na kodzie wyjścia „dpkg -s”. Na przykład, jeśli „apt zainstalowałeś” pakiet, a następnie „apt remove” go i próbowałeś „dpkg -s nazwa_pakietu”, wtedy zauważysz status: odinstaluj i wyjdź z kodu zero (tak jakby był zainstalowany). Musisz przeanalizować bro wyjściowe „dpkg -s”.
Dmitry Shevkoplyas
@DmitryShevkoplyas dzięki za raport. I nie mógł odtworzyć na Ubuntu 19.10 z: sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Czy możesz podać dalsze szczegóły?
Ciro Santilli 29 冠状 病 六四 事件 法轮功
1
w rzeczy samej - w przypadku testowym z pakietem „cześć” „dpkg -s” poprawnie pokazuje pakiet jako niezainstalowany i podaje oczekiwany kod wyjścia „1”. Ale spróbuj tego samego testu instalacji / usuń z pakietem „certbot”, wtedy zobaczysz „Status: deinstall ok config-files” jako wyjście „dpkg -s” po twoim „apt remove certbot”, a kod wyjścia nieprawidłowo pokazuje nam „0”. Moje błędne założenie było takie, że dotyczy to każdego innego pakietu, ale wydaje się, że nie jest takie samo dla wszystkich, co jest jeszcze gorsze i mniej przewidywalne. Parsuj „dpkg -s”, musisz (c) Yoda :)
Dmitry Shevkoplyas
11

Oferuję tę aktualizację, ponieważ Ubuntu dodało swoje „Personal Package Archive” (PPA), podobnie jak odpowiedź na to pytanie, a pakiety PPA mają inny wynik.

  1. Natywny pakiet repozytorium Debiana nie został zainstalowany:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Pakiet PPA zarejestrowany na hoście i zainstalowany:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Pakiet PPA zarejestrowany na hoście, ale nie zainstalowany:

    ~$ 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.

Opublikowane również na: /superuser/427318/test-if-a-package-is-installed-in-apt/427898

tahoar
źródło
2
Jeśli instalujesz i usuwasz pakiet, następnie używasz pakietu dpkg-query; echo $? będzie wynosić 0 również, jeśli pakiet nie zostanie zainstalowany.
Pol Hallen
8

UpAndAdam napisał:

Jednak w przypadku skryptów nie można polegać na kodach zwrotnych

Z mojego doświadczenia wynika, że możesz polegać na kodach wyjścia dkpg.

Kod powrotu dpkg -s wynosi 0, jeśli pakiet jest zainstalowany, i 1, jeśli nie jest, więc najprostszym rozwiązaniem, jakie znalazłem, było:

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

Działa dla mnie dobrze ...

rocka84
źródło
11
Po apt-get remove <package>, dpkg -s <package>wciąż zwraca 0, mimo że pakiet todeinstalled
ThorSummoner,
7

To wydaje się działać całkiem dobrze.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Zwraca albo 0jeśli nie jest zainstalowany, albo pewną liczbę > 0jeśli jest zainstalowany.
piaskowy człowiek
źródło
8
grep | wc -ljest antypatternem. Aby sprawdzić, czy coś istnieje, po prostu chcesz grep -q. Aby faktycznie policzyć wystąpienia (co rzadko jest przydatne w tego rodzaju scenariuszu), użyj grep -c.
tripleee
@tripleee Więc dpkg -s zip | grep -c "Package: zip"? (używając zip jako przykładowego pakietu)
David Tabernero M.
@Davdriver To nie jest dokładnie to, co powyższe, ale tak. W skrypcie prawdopodobnie chcesz grep -q 'Package: zip'zwrócić kod wyjścia, który wskazuje, czy wynik został znaleziony bez drukowania niczego.
tripleee
wydaje się, że działa również w przypadku odinstalowanych pakietów
mehmet
4

Zdecydowałem się na jeden oparty na odpowiedzi Nultyi :

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

Zasadniczo komunikat o błędzie z dpkg --get-selections jest znacznie łatwiejszy do przeanalizowania niż większość innych, ponieważ nie zawiera stanów takich jak „deinstalacja”. Może także sprawdzać wiele pakietów jednocześnie, czego nie można zrobić za pomocą samych kodów błędów.

Objaśnienie / przykład:

$ 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

Dlatego grep usuwa zainstalowane pakiety z listy, a awk wyciąga nazwy pakietów z komunikatu o błędzie, co powoduje MISSING='python3-venv python3-dev jq', że można je w prosty sposób wstawić do polecenia instalacji.

Nie wydam ślepo apt-get install $PACKAGES ponieważ jak wspomniano w komentarzach, może to nieoczekiwanie ulepszyć pakiety, których nie planowałeś; niezbyt dobry pomysł na zautomatyzowane procesy, które powinny być stabilne.

Izkata
źródło
Podoba mi się to rozwiązanie. Zwięzłe i testy dla wielu paczek jednocześnie. Ponadto możesz sprawić, by opcjonalne sprawdzenie było tak proste, jak[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk
3

Odkryłem, że wszystkie powyższe rozwiązania mogą dawać fałszywie dodatnie, jeśli pakiet zostanie zainstalowany, a następnie usunięty, ale pakiet instalacyjny pozostaje w systemie.

Aby powielić: Zainstaluj pakiet apt-get install curl
Usuń pakietapt-get remove curl

Teraz przetestuj powyższe odpowiedzi.

Następujące polecenie wydaje się rozwiązać ten warunek:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Spowoduje to ostateczne zainstalowanie lub niezainstalowanie

znak
źródło
nie do końca, niestety - inne możliwe wyniki w tym przypadku są config-files- więc myślę, że ostateczny | grep -q "installed"jest naprawdę potrzebny, aby uzyskać funkcjonalny kod wyjścia.
chętny
zrób to| grep -q '^installed$'
chętny
3

Wydaje się, że obecnie apt-getma opcję, --no-upgradektóra robi to, czego chce OP:

--no-upgradeNie aktualizuj pakietów. W połączeniu z instalacją, brak aktualizacji uniemożliwi aktualizację wymienionych pakietów, jeśli są już zainstalowane.

Strona z https://linux.die.net/man/8/apt-get

Dlatego możesz użyć

apt-get install --no-upgrade package

i packagezostanie zainstalowany tylko, jeśli nie jest.

Giovanni Mascellani
źródło
2

To wystarczy. apt-get installjest idempotentny.

sudo apt-get install command
David Baucum
źródło
5
Istnieją sytuacje, w których wykonanie apt-get installpakietu jest niepożądane, gdy pakiet jest już zainstalowany, nawet jeśli sama komenda jest idempotentna. W moim przypadku instaluję pakiet na zdalnym systemie z surowym modułem Ansible, który będzie zgłaszał zmiany systemu za każdym razem, gdy będę działał apt-get installbez rozróżnienia. Warunkowe rozwiązuje ten problem.
JBentley,
1
@JBentley To dobra uwaga. Pakiety, które są instalowane jako część zależności, zostaną oznaczone jako zainstalowane ręcznie, a następnie nie zostaną usunięte po usunięciu zależności, jeśli zostanie ona zainstalowana przez apt-get.
David Baucum,
2

Posługiwać się:

apt-cache policy <package_name>

Jeśli nie jest zainstalowany, pokaże:

Installed: none

W przeciwnym razie pokaże:

Installed: version
Mohammed Noureldin
źródło
1

Ta funkcja już istnieje w Ubuntu i Debianie, w command-not-foundpakiecie.

camh
źródło
15
matt @ matt-ubuntu: ~ $ polecenie-nie-znaleziono polecenie-nie-znaleziono: polecenie nie znaleziono ... lol.
Matt Fletcher
1
command-not-foundjest interaktywnym pomocnikiem, a nie narzędziem zapewniającym odpowiednie zależności. Oczywiście właściwym sposobem deklarowania zależności jest spakowanie oprogramowania w pakiet Debiana i prawidłowe wypełnienie Depends:deklaracji w debian/controlpliku pakietu .
tripleee
1
apt list [packagename]

wydaje się być najprostszym sposobem na zrobienie tego poza programem dpkg i starszymi narzędziami apt- *

Erich
źródło
Przydaje się do ręcznego sprawdzania, ale wydaje ostrzeżenie, że apt nie jest przeznaczony do skryptowania - w przeciwieństwie do narzędzi apt- *.
Hontvári Levente,
1
which <command>
if [ $? == 1 ]; then
    <pkg-manager> -y install <command>
fi
irock.dev
źródło
1
Powiązane, powinieneś użyć command -v <command>; Nie which <command>. Zobacz także Sprawdź, czy program istnieje ze skryptu Bash .
jww
1

Miałem podobne wymaganie, gdy uruchomiłem test lokalnie zamiast w oknie dokowanym. Zasadniczo chciałem zainstalować wszystkie znalezione pliki .deb, jeśli nie zostały jeszcze zainstalowane.

# 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

Wydaje mi się, że jedynym problemem, jaki widzę, jest to, że nie sprawdza numeru wersji pakietu, więc jeśli plik .deb jest nowszą wersją, nie zastąpiłoby to aktualnie zainstalowanego pakietu.

Craig
źródło
1

W przypadku Ubuntu apt zapewnia dość przyzwoity sposób na zrobienie tego. Poniżej znajduje się przykład dla Google Chrome:

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

Przekierowuję wyjście błędu na zero, ponieważ apt ostrzega przed użyciem jego „niestabilnego cli”. Podejrzewam, że pakiet list jest stabilny, więc myślę, że można odrzucić to ostrzeżenie. -Qq czyni apt super cichym.

carlin.scott
źródło
1
to nie będzie działać poprawnie, jeśli coś można „zaktualizować”
Paweł Barcik
@PawelBarcik dobry punkt. Zaktualizowałem odpowiedź, aby poradzić sobie z tą sytuacją.
carlin.scott
0

To polecenie jest najbardziej niezapomniane:

dpkg --get-selections <package-name>

Jeśli jest zainstalowany, drukuje:

<nazwa-pakietu> zainstaluj

W przeciwnym razie drukuje się

Nie znaleziono pakietów pasujących do <nazwa_pakietu>.

Zostało to przetestowane na Ubuntu 12.04.1 (Precise Pangolin).

iNulty
źródło
4
dpkg --get-selections <package-name>nie ustawia kodu wyjścia na wartość niezerową, gdy pakiet nie zostanie znaleziony.
Lucas
0

Wiele rzeczy zostało powiedzianych, ale dla mnie najprostszym sposobem jest:

dpkg -l | grep packagename
freedev
źródło
0

W Bash:

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

Pamiętaj, że możesz mieć ciąg znaków z kilkoma pakietami w PKG.

daruma
źródło
0

Używam następującego sposobu:

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
źródło