Controlla se è installato un pacchetto apt-get e installalo se non è su Linux


223

Sto lavorando su un sistema Ubuntu e attualmente questo è quello che sto facendo:

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

È questo che farebbe la maggior parte delle persone? O c'è una soluzione più elegante?


7
I nomi dei comandi non riflettono sempre il nome del pacchetto a cui appartengono. Qual è il tuo obiettivo più grande? Perché non provi semplicemente a installarlo, e nel peggiore dei casi non lo farà, dal momento che è già installato.
viam0Zah,

8
Fortunatamente, apt-get install è idempotente, quindi è sicuro eseguirlo e non preoccuparsi se è installato o meno.
David Baucum,

Il commento di DavidBaucum dovrebbe essere una risposta che otterrebbe il maggior numero di voti.
Nirmal,

@Nirmal, risposta fatta.
David Baucum,

1
Correlati, è necessario utilizzare command -v <command>; no which <command>. Vedi anche Verifica se esiste un programma da uno script Bash .
17-17

Risposte:


314

Per verificare se è packagenamestato installato, digitare:

dpkg -s <packagename>

Puoi anche usare dpkg-queryun output più ordinato per il tuo scopo e accettare anche i caratteri jolly.

dpkg-query -l <packagename>

Per trovare quale pacchetto possiede il command, prova:

dpkg -S `which <command>`

Per ulteriori dettagli, vedere l'articolo Scopri se il pacchetto è installato in Linux e chp sheet di dpkg .


32
Se come persona lo desideri NON a livello di programmazione, puoi utilizzare queste informazioni così come sono. Tuttavia, non è possibile fare semplicemente affidamento sui codici di ritorno qui per gli script o sull'output / mancanza di output da soli per gli script. Dovresti scansionare l'output di questi comandi, limitandone l'utilità per questa domanda.
UpAndAdam

4
Stranamente, ho scoperto di recente che dpkg-query usato per restituire 1 su un pacchetto mancante, ora (Ubuntu 12.04) restituisce 0, causando ogni sorta di problemi sul mio script di configurazione del nodo build jenkins! dpkg -s restituisce 0 sul pacchetto installato e 1 sul pacchetto non installato.
Therealstubot,

18
Ehi, OP ha chiesto l' ifuso. Sto anche cercando l' ifuso.
Tomáš Zato - Ripristina Monica il

1
@Therealstubot: sto anche usando Ubuntu 12.04 e dpkg -srestituisce 1 sui pacchetti mancanti e 0 altrimenti, come dovrebbe. In che modo differiva nelle versioni precedenti (o recenti)?
MestreLion,

4
una nota: dpkg -srestituisce zero se un pacchetto è stato installato e quindi rimosso - in tal caso è Status: deinstall ok config-fileso simile, quindi è "ok" - quindi per me questo non è un test sicuro. dpkg-query -lnon sembra restituire un risultato utile neanche in questo caso.
acuto dal

86

Per essere un po 'più esplicito, ecco un po' di script bash che verifica la presenza di un pacchetto e lo installa, se necessario. Naturalmente, puoi fare altre cose scoprendo che il pacchetto è mancante, come semplicemente uscire con un codice di errore.

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

Se lo script viene eseguito all'interno di una GUI (ad es. È uno script Nautilus), probabilmente vorrai sostituire l'invocazione 'sudo' con una 'gksudo'.


5
--force-yessembra una cattiva idea. Dalla pagina man: "Questa è un'opzione pericolosa che farà in modo che apt-get continui senza chiedere se sta facendo qualcosa di potenzialmente dannoso. Non dovrebbe essere usato se non in situazioni molto speciali. L'uso di --force-yes può potenzialmente distruggere il tuo sistema !" Usarlo in uno script rende ancora peggio.
riduzione dell'attività

68

Questo one-liner restituisce 1 (installato) o 0 (non installato) per il pacchetto 'nano'.

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

anche se il pacchetto non esiste / non è disponibile.

L'esempio seguente installa il pacchetto 'nano' se non è installato.

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi

4
La mia variazione su questo:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner,

@ThorSummoner: ti interessa spiegare perché il tuo è meglio?
Knocte,

1
@knocte Non sono sicuro che ci sia un argomento su come essere oggettivamente migliori. Anche se sono sicuro che il one-liner testuale del post eseguirà l'output del risultato, che non vorrei lasciare ciondolare in una risposta. L'unica linea che mostro esemplifica ottenere (stampare) solo il codice di uscita.
ThorSummoner,

1
@ThorSummoner Non hai bisogno grep -Pdi una regex semplice come quella.
Tripleee

7
Più semplice: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Non c'è bisogno di usare grep -c, basta usare lo stato di uscita digrep
Stephen Ostermiller,

17

dpkg -s utilizzo programmatico con installazione automatica

Mi piace dpkg -sin quanto esce con stato 1se uno dei pacchetti non è installato, rendendo semplice l'automazione:

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

man dpkg purtroppo non documenta lo stato di uscita, ma penso che dovrebbe essere ragionevolmente sicuro fare affidamento su di esso:

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

Una cosa da notare è che in esecuzione:

sudo apt remove <package-name>

non rimuove necessariamente immediatamente tutti i file per alcuni pacchetti (ma lo fa per altri, non sai perché?) e contrassegna semplicemente il pacchetto per la rimozione.

In questo stato, il pacchetto sembra essere ancora utilizzabile e poiché i suoi file sono ancora presenti, ma è contrassegnato per la rimozione in seguito.

Ad esempio se esegui:

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

poi:

  • le prime due echo $?uscite 0, solo la terza uscita1

  • l'output per il primo dpkg -s certbotcontiene:

    Status: deinstall ok installed

    mentre il secondo dice:

    Status: deinstall ok config-files

    e scompare solo dopo l'eliminazione:

    dpkg-query: package 'certbot' is not installed and no information is available
  • il file /etc/logrotate.d/certbotè ancora presente nel sistema dopo apt remove, ma non dopo --purge.

    Tuttavia, il file /usr/lib/python3/dist-packages/certbot/reporter.pyè ancora presente anche dopo --purge.

Non capisco perché, ma con il hellopacchetto il secondo dpkgdopo apt removemostra che il pacchetto è già stato rimosso senza --purge:

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

Le documentazioni sono anche poco chiare, ad esempio:

sudo apt dselect-upgrade

non è stato rimosso certbotquando è stato contrassegnato come deinstall, anche se man apt-getsembra indicare che:

dselect-upgradeè usato insieme al tradizionale front-end di packaging Debian, dselect (1). dselect-upgrade segue le modifiche apportate da dselect (1) al campo Stato dei pacchetti disponibili ed esegue le azioni necessarie per realizzare quello stato (ad esempio, la rimozione di vecchi e l'installazione di nuovi pacchetti).

Guarda anche:

Testato su Ubuntu 19.10.

aptPacchetto Python

Esiste un pacchetto Python 3 preinstallato chiamato aptin Ubuntu 18.04 che espone un'interfaccia apt Python!

Uno script che controlla se un pacchetto è installato e lo installa se non è visibile in: Come installare un pacchetto usando l'API python-apt

Ecco una copia per riferimento:

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

Controllare se un eseguibile è PATHinvece

Vedi: Come posso verificare se esiste un programma da uno script Bash?


Ciro, non puoi fare affidamento sul codice di uscita "dpkg -s". Ad esempio, se hai "apt installato" un pacchetto, quindi "apt rimuoverlo" e hai provato a "dpkg -s nomepacchetto", noterai lo stato: deinstall e exit code zero (come se installato). Devi analizzare l'output "dpkg -s".
Dmitry Shevkoplyas,

@DmitryShevkoplyas grazie per la segnalazione. Non riuscivo a riprodurre su Ubuntu 19.10 con: sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Potete fornire ulteriori dettagli?
Ciro Santilli 29 冠状 病 六四 事件 法轮功

1
infatti - per il tuo caso di test con il pacchetto "ciao" "dpkg -s" mostra correttamente il pacchetto come non installato e fornisce il codice di uscita previsto "1". Ma prova lo stesso instal / remove check con il pacchetto "certbot", quindi vedrai "Status: deinstall ok config-files" come l'output "dpkg -s" dopo il tuo "apt remove certbot" e il codice di uscita ci mostra erroneamente "0". La mia ipotesi sbagliata era che è il caso esatto per qualsiasi altro pacchetto, ma sembra che non sia lo stesso per tutti, che è anche peggio e meno prevedibile. Parse "dpkg -s" devi (c) Yoda :)
Dmitry Shevkoplyas

11

Offro questo aggiornamento da quando Ubuntu ha aggiunto il suo "Personal Package Archive" (PPA) proprio come è stata data una risposta a questa domanda, e i pacchetti PPA hanno un risultato diverso.

  1. Pacchetto repository Debian nativo non installato:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Pacchetto PPA registrato su host e installato:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Pacchetto PPA registrato sull'host ma non installato:

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

Pubblicato anche su: /superuser/427318/test-if-a-package-is-installed-in-apt/427898


2
Se installi e rimuovi un pacchetto, successivamente usi il pacchetto dpkg-query; echo $? sarà 0 anche se il pacchetto non è installato.
Pol Hallen,

8

UpAndAdam ha scritto:

Tuttavia, non puoi semplicemente fare affidamento sui codici di ritorno qui per gli script

Nella mia esperienza puoi fare affidamento sui codici di uscita di dkpg.

Il codice di ritorno di dpkg -s è 0 se il pacchetto è installato e 1 se non lo è, quindi la soluzione più semplice che ho trovato è stata:

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

Funziona bene per me ...


11
Dopo apt-get remove <package>, dpkg -s <package>restituisce ancora 0, anche se il pacchetto èdeinstalled
ThorSummoner il

7

Questo sembra funzionare abbastanza bene.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Restituisce 0se non installato o un numero > 0se installato.

8
grep | wc -lè un antipasto. Per verificare se esiste qualcosa, vuoi semplicemente grep -q. Per contare effettivamente le occorrenze (che è raramente utile in questo tipo di scenario), utilizzare grep -c.
Tripleee

@tripleee Così, dpkg -s zip | grep -c "Package: zip"? (usando zip come pacchetto di esempio)
David Tabernero M.

@Davdriver Non è esattamente ciò che fa sopra, ma sì. In uno script probabilmente si desidera grep -q 'Package: zip'restituire un codice di uscita che indica se il risultato è stato trovato o meno senza stampare nulla.
Tripleee

questo sembra funzionare bene anche per i pacchetti disinstallati
mehmet

4

Ne ho optato per uno basato sulla risposta di 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

Fondamentalmente, il messaggio di errore da dpkg --get-selectionsè molto più facile da analizzare rispetto alla maggior parte degli altri, perché non include stati come "deinstall". Può anche controllare più pacchetti contemporaneamente, cosa che non si può fare con i soli codici di errore.

Spiegazione / Esempio:

$ 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

Quindi grep rimuove dall'elenco i pacchetti installati e awk estrae i nomi dei pacchetti dal messaggio di errore, risultando MISSING='python3-venv python3-dev jq', che può essere banalmente inserito in un comando di installazione.

Non sto pubblicando ciecamente un apt-get install $PACKAGESperché, come menzionato nei commenti, questo può inaspettatamente aggiornare i pacchetti su cui non stavate pianificando; non è davvero una buona idea per i processi automatizzati che dovrebbero essere stabili.


Mi piace questa soluzione. Conciso e test per più pacchetti contemporaneamente. Inoltre, potresti rendere il controllo opzionale qualcosa di semplice come[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk,

3

Ho trovato che tutte le soluzioni sopra possono produrre un falso positivo se un pacchetto è installato e quindi rimosso, ma il pacchetto di installazione rimane sul sistema.

Per replicare: Installa pacchetto apt-get install curl
Rimuovi pacchettoapt-get remove curl

Ora prova sopra le risposte.

Il seguente comando sembra risolvere questa condizione:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Ciò comporterà l' installazione o la non installazione definitiva


non del tutto, purtroppo - altri possibili risultati in questo caso sono config-files- quindi penso che | grep -q "installed"sia davvero necessario un finale per ottenere uno stato di codice di uscita funzionale.
appassionato il

rendilo| grep -q '^installed$'
appassionato il

3

Sembra che al giorno d'oggi apt-getabbia un'opzione --no-upgradeche fa proprio quello che l'OP vuole:

--no-upgradeNon aggiornare i pacchetti. Se utilizzato insieme all'installazione, nessun aggiornamento impedirà l'aggiornamento dei pacchetti elencati se sono già installati.

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

Pertanto è possibile utilizzare

apt-get install --no-upgrade package

e packageverrà installato solo in caso contrario.



2

Questo lo farà. apt-get installè idempotente.

sudo apt-get install command

5
Esistono scenari in cui non è consigliabile eseguire un apt-get installpacchetto su un pacchetto già installato, anche se il comando stesso è idempotente. Nel mio caso, sto installando un pacchetto su un sistema remoto con il modulo raw di Ansible, che riporterà il sistema come modificato ogni volta se eseguo apt-get installindiscriminatamente. Un condizionale risolve quel problema.
JBentley,

1
@JBentley Questo è un buon punto. I pacchetti installati come parte di una dipendenza verranno contrassegnati come installati manualmente, quindi non verranno rimossi quando viene rimossa la dipendenza se apt-get lo installa.
David Baucum,

2

Uso:

apt-cache policy <package_name>

Se non è installato, mostrerà:

Installed: none

Altrimenti mostrerà:

Installed: version

1

Questa funzione esiste già in Ubuntu e Debian, nel command-not-foundpacchetto.


15
matt @ matt-ubuntu: ~ $ comando-non-trovato comando-non-trovato: comando non trovato ... lol.
Matt Fletcher,

1
command-not-foundè un aiuto interattivo, non uno strumento per assicurarti di avere le dipendenze che desideri. Ovviamente, il modo corretto di dichiarare le dipendenze è impacchettare il software in un pacchetto Debian e compilare correttamente la Depends:dichiarazione nel debian/controlfile del pacchetto .
Tripleee

1
apt list [packagename]

sembra essere il modo più semplice per farlo al di fuori di dpkg e strumenti apt- * precedenti


È utile per il controllo manuale, ma emette un avviso in cui si dice che apt non è destinato allo scripting, al contrario degli strumenti apt- *.
Hontvári Levente,


1

Avevo un requisito simile quando eseguivo il test localmente anziché nella finestra mobile. In pratica volevo solo installare i file .deb trovati se non erano già installati.

# 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

Immagino che l'unico problema che vedo sia che non controlla il numero di versione del pacchetto, quindi se il file .deb è una versione più recente, questo non sovrascriverebbe il pacchetto attualmente installato.


1

Per Ubuntu, apt fornisce un modo abbastanza decente per farlo. Di seguito è riportato un esempio per google chrome:

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

Sto reindirizzando l'output dell'errore su null perché apt avverte di non usare il suo "cli instabile". Ho il sospetto che il pacchetto dell'elenco sia stabile, quindi penso che sia giusto gettare via questo avviso. Il -qq rende apt molto silenzioso.


1
questo non funzionerà correttamente se qualcosa è "aggiornabile"
Pawel Barcik,

@PawelBarcik buon punto. Ho aggiornato la risposta per gestire quella situazione.
carlin.scott,

0

Questo comando è il più memorabile:

dpkg --get-selections <package-name>

Se è installato, stampa:

<nome-pacchetto> installa

Altrimenti stampa

Nessun pacchetto trovato corrispondente a <nome-pacchetto>.

Questo è stato testato su Ubuntu 12.04.1 (Precise Pangolin).


4
dpkg --get-selections <package-name>non imposta il codice di uscita su un valore diverso da zero quando il pacchetto non viene trovato.
Lucas,

0

Molte cose sono state dette ma per me il modo più semplice è:

dpkg -l | grep packagename

0

In Bash:

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

Nota che puoi avere una stringa con diversi pacchetti in PKG.


0

Uso il seguente modo:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.