Verifique se um pacote apt-get está instalado e instale-o se não estiver no Linux

223

Estou trabalhando em um sistema Ubuntu e atualmente é o que estou fazendo:

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

É isso que a maioria das pessoas faria? Ou existe uma solução mais elegante?

John Jiang
fonte
7
Os nomes dos comandos nem sempre refletem o nome do pacote ao qual pertencem. Qual é o seu objetivo maior? Por que você simplesmente não tenta instalá-lo e, na pior das hipóteses, não, pois já está instalado.
Viam0Zah
8
Felizmente, o apt-get install é idempotente, por isso é seguro executá-lo e não se preocupar se ele está instalado ou não.
David Baucum 31/03
O comentário de @ DavidBaucum deve ser uma resposta que receba mais votos.
Nirmal 03/04
@Nirmal, resposta feita.
David Baucum
1
Relacionado, você deve usar command -v <command>; não which <command>. Consulte também Verifique se existe um programa a partir de um script Bash .
JWW

Respostas:

314

Para verificar se packagenamefoi instalado, digite:

dpkg -s <packagename>

Você também pode usar um dpkg-queryque tenha uma saída mais limpa para o seu propósito e aceite curingas também.

dpkg-query -l <packagename>

Para descobrir qual pacote é o proprietário command, tente:

dpkg -S `which <command>`

Para obter mais detalhes, consulte o artigo Descubra se o pacote está instalado no Linux e no dpkg cheat sheet .

viam0Zah
fonte
32
Se você como pessoa deseja isso de forma NÃO programática, pode usar essas informações como estão. No entanto, você não pode simplesmente confiar nos códigos de retorno aqui para scripts ou na saída / falta de saída apenas para scripts. Você precisaria verificar a saída desses comandos, limitando sua utilidade para esta pergunta.
UpAndAdam
4
Curiosamente, eu descobri recentemente que o dpkg-query costumava retornar 1 em um pacote ausente, agora (Ubuntu 12.04) retorna 0, causando todo tipo de problemas no script de configuração do nó de construção dos jenkins! O dpkg -s retorna 0 no pacote instalado e 1 no pacote não instalado.
precisa saber é o seguinte
18
Ei, OP pediu pelo ifuso. Eu também estou procurando o ifuso.
Tomáš Zato - Restabelece Monica
1
@Therealstubot: Eu também estou usando o Ubuntu 12.04 e dpkg -sretorna 1 nos pacotes ausentes e 0 caso contrário, como deveria. Como foi diferente nas versões anteriores (ou recentes)?
MestreLion
4
uma nota: dpkg -sretorna zero se um pacote foi instalado e removido - nesse caso, é Status: deinstall ok config-filessemelhante ou similar, então está "ok" - então, para mim, esse não é um teste seguro. dpkg-query -ltambém não parece retornar um resultado útil neste caso.
afiado
86

Para ser um pouco mais explícito, aqui está um script bash que verifica se há um pacote e o instala, se necessário. Obviamente, você pode fazer outras coisas ao descobrir que o pacote está ausente, como simplesmente sair com um código de erro.

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 o script for executado em uma GUI (por exemplo, é um script do Nautilus), você provavelmente desejará substituir a chamada 'sudo' por uma chamada 'gksudo'.

Urhixidur
fonte
5
--force-yesparece uma péssima ideia. Da página de manual: "Esta é uma opção perigosa que fará com que o apt-get continue sem avisar se estiver fazendo algo potencialmente prejudicial. Ele não deve ser usado, exceto em situações muito especiais. O uso de --force-yes pode potencialmente destruir seu sistema ! " Usá-lo em um script torna ainda pior.
reduzindo a atividade
68

Este one-liner retorna 1 (instalado) ou 0 (não instalado) para o pacote 'nano'.

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

mesmo que o pacote não exista / não esteja disponível.

O exemplo abaixo instala o pacote 'nano' se não estiver instalado.

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
Seb
fonte
4
Meu variação sobre este:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner
@ ThorSummoner: gostaria de explicar por que o seu é melhor?
knocte
1
@knocte Não tenho certeza de que haja um argumento a ser objetivamente melhor. Embora eu esteja confiante de que a única linha da postagem literal executará a saída do resultado, que eu não gostaria de deixar pendente em uma resposta. O forro que mostro exemplifica a obtenção (impressão) apenas do código de saída.
23416 ThorSummoner
1
@ ThorSummoner Você não precisa grep -Pde um regex simples como esse.
Tripleee
7
Mais simples: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Não há necessidade de usar grep -c, basta usar o status de saída degrep
Stephen Ostermiller 5/17/17
17

dpkg -s uso programático com instalação automática

Gosto dpkg -sque ele saia com status 1se algum dos pacotes não estiver instalado, facilitando a automação:

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

man dpkg infelizmente não documenta o status de saída, mas acho que deve ser razoavelmente seguro confiar nele:

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

Uma coisa a notar é que executando:

sudo apt remove <package-name>

não remove necessariamente todos os arquivos imediatamente para alguns pacotes (mas remove outros, não sabe por quê?) e apenas marca o pacote para remoção.

Nesse estado, o pacote parece ainda ser utilizável e como seus arquivos ainda estão presentes, mas é marcado para remoção posteriormente.

Por exemplo, se você executar:

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

então:

  • as duas primeiras echo $?saídas 0, apenas a terceira produz1

  • a saída para o primeiro dpkg -s certbotcontém:

    Status: deinstall ok installed

    enquanto o segundo diz:

    Status: deinstall ok config-files

    e só desaparece após a limpeza:

    dpkg-query: package 'certbot' is not installed and no information is available
  • o arquivo /etc/logrotate.d/certbotainda está presente no sistema depois apt remove, mas não depois --purge.

    No entanto, o arquivo /usr/lib/python3/dist-packages/certbot/reporter.pyainda está presente mesmo depois --purge.

Eu não entendo o porquê, mas com o hellopacote o segundo dpkgdepois apt removemostra que ele já foi removido sem --purge:

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

As documentações também não são claras, por exemplo:

sudo apt dselect-upgrade

não removeu certbotquando foi marcado como deinstall, embora man apt-getpareça indicar que:

dselect-upgradeé usado em conjunto com o front-end tradicional de empacotamento Debian, dselect (1). O dselect-upgrade segue as alterações feitas pelo dselect (1) no campo Status dos pacotes disponíveis e executa as ações necessárias para realizar esse estado (por exemplo, a remoção de antigos e a instalação de novos pacotes).

Veja também:

Testado no Ubuntu 19.10.

aptPacote Python

Existe um pacote Python 3 pré-instalado chamado aptUbuntu 18.04 que expõe uma interface apt do Python!

Um script que verifica se um pacote está instalado e o instala se não puder pode ser visto em: Como instalar um pacote usando a API python-apt

Aqui está uma cópia para referência:

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

Verifique se um executável está no PATHlugar

Consulte: Como posso verificar se existe um programa a partir de um script Bash?

Ciro Santilli adicionou uma nova foto
fonte
Ciro, você não pode confiar no código de saída "dpkg -s". Por exemplo, se você "instalou" um pacote, "apt remove" e tentou "dpkg -s packagename", então você notará o status: desinstale e saia do código zero (como se estivesse instalado). Você precisa analisar o bro de saída "dpkg -s".
Dmitry Shevkoplyas 28/01
@DmitryShevkoplyas obrigado pelo relatório. Eu não poderia reproduzir no Ubuntu 19.10 com: sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Você pode fornecer mais detalhes?
Ciro Santilli escreveu em
1
de fato - para o seu caso de teste com o pacote "hello", "dpkg -s" mostra corretamente o pacote como não instalado e fornece o código de saída esperado "1". Mas tente a mesma verificação de instalação / remoção com o pacote "certbot", e você verá "Status: deinstall ok config-files" como a saída "dpkg -s" após o seu "apt remove certbot" e o código de saída nos mostra incorretamente "0" Minha suposição errada foi de que é o caso exato de qualquer outro pacote, mas parece que não é o mesmo para todos, o que é ainda pior e menos previsível. Analisar "dpkg -s" você deve (c) Yoda :)
Dmitry Shevkoplyas 29/01
11

Eu ofereço esta atualização, pois o Ubuntu adicionou seu "Personal Package Archive" (PPA) exatamente quando esta pergunta foi respondida, e os pacotes PPA têm um resultado diferente.

  1. Pacote de repositório Debian nativo não instalado:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Pacote PPA registrado no host e instalado:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Pacote PPA registrado no host, mas não instalado:

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

Também publicado em: /superuser/427318/test-if-a-package-is-installed-in-apt/427898

tahoar
fonte
2
Se você instalar e remover um pacote, use o pacote dpkg-query; eco $? será 0 também se o pacote não estiver instalado.
Pol Hallen 22/09
8

UpAndAdam escreveu:

No entanto, você não pode simplesmente confiar nos códigos de retorno aqui para criar scripts

Na minha experiência, você pode confiar nos códigos de saída do dkpg.

O código de retorno do dpkg -s é 0 se o pacote estiver instalado e 1 se não estiver, então a solução mais simples que encontrei foi:

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

Funciona bem para mim ...

rocka84
fonte
11
Depois apt-get remove <package>, dpkg -s <package>ainda retorna 0, mesmo que o pacote édeinstalled
ThorSummoner
7

Isso parece funcionar muito bem.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Ele retorna 0se não estiver instalado ou algum número, > 0se instalado.
sandman
fonte
8
grep | wc -lé um antipadrão. Para verificar se existe algo, você quer simplesmente grep -q. Para contar as ocorrências (o que raramente é útil nesse tipo de cenário), use grep -c.
Tripleee
@tripleee Então dpkg -s zip | grep -c "Package: zip",? (usando zip como pacote de amostra)
David Tabernero M.
@ Davdriver Isso não é exatamente o que faz acima, mas sim. Em um script, você provavelmente deseja grep -q 'Package: zip'retornar um código de saída que indica se o resultado foi ou não encontrado sem imprimir nada.
tripleee
isto parece trabalhar bem para pacotes desinstalados também
Mehmet
4

Eu decidi um baseado na resposta de 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

Basicamente, a mensagem de erro de dpkg --get-selectionsé muito mais fácil de analisar do que a maioria das outras, porque não inclui status como "deinstall". Ele também pode verificar vários pacotes simultaneamente, algo que você não pode fazer apenas com códigos de erro.

Explicação / exemplo:

$ 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

Portanto, o grep remove os pacotes instalados da lista e o awk retira os nomes dos pacotes da mensagem de erro, resultando em MISSING='python3-venv python3-dev jq', que pode ser trivialmente inserido em um comando de instalação.

Não estou emitindo cegamente apt-get install $PACKAGESporque, como mencionado nos comentários, isso pode atualizar inesperadamente pacotes que você não estava planejando; não é realmente uma boa ideia para processos automatizados que se espera sejam estáveis.

Izkata
fonte
Eu gosto desta solução. Conciso e testes para vários pacotes ao mesmo tempo. Além disso, você pode fazer com que a verificação opcional seja algo tão simples quanto[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk
3

Eu descobri que todas as soluções acima podem produzir um falso positivo se um pacote estiver instalado e, em seguida, removido, mas o pacote de instalação permanece no sistema.

Para replicar: Instalar pacote apt-get install curl
Remover pacoteapt-get remove curl

Agora teste as respostas acima.

O comando a seguir parece resolver essa condição:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Isso resultará em uma instalação definitiva ou não instalada

Marca
fonte
não inteiramente, infelizmente - outros resultados possíveis nesse caso são config-files-, então acho que uma final | grep -q "installed"é realmente necessária para obter um status de código de saída funcional.
afiado
Faça isso| grep -q '^installed$'
interessado
3

Parece que hoje em dia apt-gethá uma opção --no-upgradeque apenas faz o que o OP deseja:

--no-upgradeNão atualize pacotes. Quando usado em conjunto com a instalação, o no-upgrade impedirá que os pacotes listados sejam atualizados se já estiverem instalados.

Página de manual de https://linux.die.net/man/8/apt-get

Portanto, você pode usar

apt-get install --no-upgrade package

e packageserá instalado apenas se não estiver.

Giovanni Mascellani
fonte
2

Isso fará isso. apt-get installé idempotente.

sudo apt-get install command
David Baucum
fonte
5
Existem cenários em que fazer apt-get installum pacote é indesejável onde o pacote já está instalado, mesmo que o próprio comando seja idempotente. No meu caso, estou instalando um pacote em um sistema remoto com o módulo bruto do Ansible, que relatará o sistema como sempre alterado se eu executar apt-get installindiscriminadamente. Um condicional resolve esse problema.
JBentley
1
@JBentley Esse é um bom ponto. Os pacotes que são instalados como parte de uma dependência serão marcados como instalados manualmente e, em seguida, não serão removidos quando a dependência for removida, se você o instalar.
David Baucum
2

Usar:

apt-cache policy <package_name>

Se não estiver instalado, mostrará:

Installed: none

Caso contrário, ele mostrará:

Installed: version
Mohammed Noureldin
fonte
1

Esse recurso já existe no Ubuntu e Debian, no command-not-foundpacote.

camh
fonte
15
matt @ matt-ubuntu: ~ $ command-not-found comando-not-found: comando não encontrado ... lol.
Matt Fletcher
1
command-not-foundé um auxiliar interativo, não uma ferramenta para garantir que você tenha as dependências desejadas. Obviamente, a maneira correta de declarar dependências é empacotar seu software em um pacote Debian e preencher a Depends:declaração no debian/controlarquivo do pacote corretamente.
Tripleee
1
apt list [packagename]

parece ser a maneira mais simples de fazer isso fora do dpkg e das ferramentas apt- * mais antigas

Erich
fonte
É bom para verificação manual, mas emite um aviso informando que o apt não se destina a scripts - em contraste com as ferramentas do apt- *.
Hontvári Levente
1

Eu tinha um requisito semelhante ao executar o teste localmente, em vez de no docker. Basicamente, eu só queria instalar os arquivos .deb encontrados, se ainda não estavam instalados.

# 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

Eu acho que o único problema que vejo é que ele não verifica o número da versão do pacote, portanto, se o arquivo .deb for uma versão mais recente, isso não substituirá o pacote atualmente instalado.

Craig
fonte
1

Para o Ubuntu, o apt fornece uma maneira bastante decente de fazer isso. Abaixo está um exemplo para o google chrome:

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

Estou redirecionando a saída de erro para null, porque o apt adverte contra o uso de seu "cli instável". Eu suspeito que o pacote da lista seja estável, então acho que não há problema em jogar esse aviso fora. O -qq torna o apt super silencioso.

carlin.scott
fonte
1
isso não vai funcionar corretamente se algo é "atualizável"
Pawel Barcik
@PawelBarcik good point. Atualizei a resposta para lidar com essa situação.
Carlin.scott 29/11/19
0

Este comando é o mais memorável:

dpkg --get-selections <package-name>

Se estiver instalado, ele imprime:

instalação do <package-name>

Caso contrário, ele imprime

Não foram encontrados pacotes correspondentes ao <package-name>.

Isso foi testado no Ubuntu 12.04.1 (Precise Pangolin).

iNulty
fonte
4
dpkg --get-selections <package-name>não define o código de saída como diferente de zero quando o pacote não é encontrado.
Lucas
0

Muitas coisas foram ditas, mas para mim a maneira mais simples é:

dpkg -l | grep packagename
freedev
fonte
0

No Bash:

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

Observe que você pode ter uma string com vários pacotes no PKG.

daruma
fonte
0

Eu uso da seguinte maneira:

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
fonte