Compruebe si hay un paquete apt-get instalado y luego instálelo si no está en Linux

223

Estoy trabajando en un sistema Ubuntu y actualmente esto es lo que estoy haciendo:

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

¿Es esto lo que la mayoría de la gente haría? ¿O hay una solución más elegante?

John Jiang
fuente
77
Los nombres de los comandos no siempre reflejan el nombre del paquete al que pertenecen. ¿Cuál es tu objetivo más grande? ¿Por qué no intentas simplemente instalarlo y, en el peor de los casos, no lo hará, ya que ya está instalado?
viam0Zah
8
Afortunadamente, apt-get install es idempotente, por lo que es seguro simplemente ejecutarlo y no preocuparse de si está instalado o no.
David Baucum
El comentario de @ DavidBaucum debería ser una respuesta que obtenga la mayor cantidad de votos.
Nirmal
@Nirmal, respuesta hecha.
David Baucum
1
Relacionado, debes usar command -v <command>; no which <command>. También vea Verificar si existe un programa desde un script Bash .
jww

Respuestas:

314

Para verificar si packagenamese instaló, escriba:

dpkg -s <packagename>

También puede usar el dpkg-queryque tenga una salida más ordenada para su propósito y también acepte comodines.

dpkg-query -l <packagename>

Para encontrar qué paquete posee command, intente:

dpkg -S `which <command>`

Para más detalles, vea el artículo Averigüe si el paquete está instalado en Linux y dpkg cheat sheet .

viam0Zah
fuente
32
Si usted como persona desea esto NO programáticamente, puede usar esta información tal como está. Sin embargo, no puede simplemente confiar en los códigos de retorno aquí para las secuencias de comandos o la salida / falta de salida solo para las secuencias de comandos. Tendría que escanear la salida de estos comandos, limitando su utilidad para esta pregunta.
UpAndAdam
44
Por extraño que parezca, recientemente descubrí que dpkg-query solía devolver 1 en un paquete faltante, ahora (Ubuntu 12.04) devuelve 0, ¡causando todo tipo de problemas en mi script de configuración de nodo de compilación jenkins! dpkg -s devuelve 0 en el paquete instalado y 1 en el paquete no instalado.
Therealstubot
18
Hola, OP pidió ifuso. También estoy buscando ifuso.
Tomáš Zato - Restablece a Monica el
1
@Therealstubot: también estoy usando Ubuntu 12.04 y dpkg -sdevuelve 1 en paquetes faltantes y 0 en caso contrario, como debería ser. ¿Cómo fue diferente en versiones anteriores (o recientes)?
MestreLion
44
una nota: dpkg -sdevuelve cero si se instaló un paquete y luego se eliminó, en ese caso es Status: deinstall ok config-fileso similar, por lo que está "bien", así que para mí, esta no es una prueba segura. dpkg-query -ltampoco parece devolver un resultado útil en este caso.
interesado el
86

Para ser un poco más explícito, aquí hay un poco de script bash que busca un paquete y lo instala si es necesario. Por supuesto, puede hacer otras cosas al descubrir que falta el paquete, como simplemente salir con un código de error.

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

Si el script se ejecuta dentro de una GUI (por ejemplo, es un script de Nautilus), es probable que desee reemplazar la invocación 'sudo' por una 'gksudo'.

Urhixidur
fuente
55
--force-yesParece una mala idea. De la página de manual: "Esta es una opción peligrosa que hará que apt-get continúe sin avisar si está haciendo algo potencialmente dañino. No debe usarse excepto en situaciones muy especiales. El uso de --force-yes puede potencialmente destruir su sistema ! " Usarlo en un script lo hace aún peor.
Reducción de la actividad
68

Este one-liner devuelve 1 (instalado) o 0 (no instalado) para el paquete 'nano'.

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

incluso si el paquete no existe / no está disponible.

El siguiente ejemplo instala el paquete 'nano' si no está instalado.

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
Seb
fuente
44
Mi variación sobre esto:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner
@ThorSummoner: ¿te gustaría explicar por qué el tuyo es mejor?
Knocte
1
@knocte No estoy seguro de que haya una discusión sobre ser objetivamente mejor. Aunque estoy seguro de que la línea de la publicación literal ejecutará la salida del resultado, que no me gustaría dejar colgando en una respuesta. El delineador que muestro ejemplifica obtener (imprimir) solo el código de salida.
ThorSummoner
1
@ThorSummoner No necesita grep -Puna expresión regular simple como esa.
tripleee
77
Más simple: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- No es necesario usar grep -c, solo use el estado de salida degrep
Stephen Ostermiller
17

dpkg -s uso programático con instalación automática

Me gusta dpkg -sya que sale con estado 1si alguno de los paquetes no está instalado, lo que facilita la automatización:

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

man dpkg lamentablemente no documenta el estado de salida, pero creo que debería ser razonablemente seguro confiar en él:

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

Una cosa a tener en cuenta es que se ejecuta:

sudo apt remove <package-name>

no necesariamente elimina todos los archivos de inmediato para algunos paquetes (pero lo hace para otros, ¿no está seguro de por qué?), y solo marca el paquete para su eliminación.

En este estado, el paquete parece seguir siendo utilizable, y como sus archivos todavía están presentes, pero está marcado para su eliminación más adelante.

Por ejemplo, si ejecuta:

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

luego:

  • los dos primeros echo $?resultados0 , solo la tercera salida1

  • la salida para el primero dpkg -s certbotcontiene:

    Status: deinstall ok installed

    mientras que el segundo dice:

    Status: deinstall ok config-files

    y solo desaparece después de la purga:

    dpkg-query: package 'certbot' is not installed and no information is available
  • el archivo /etc/logrotate.d/certbottodavía está presente en el sistema después deapt remove , pero no después --purge.

    Sin embargo, el archivo /usr/lib/python3/dist-packages/certbot/reporter.pytodavía está presente incluso después --purge.

No entiendo por qué, pero con el hellopaquete el segundo dpkgdespués apt removemuestra que el paquete ya se ha eliminado sin --purge:

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

Las documentaciones también son muy poco claras, por ejemplo:

sudo apt dselect-upgrade

no se eliminó certbotcuando se marcó como deinstall, aunque man apt-getparece indicar que:

dselect-upgradese usa en conjunto con el front-end de empaquetado tradicional de Debian, dselect (1). dselect-upgrade sigue los cambios realizados por dselect (1) en el campo Estado de los paquetes disponibles y realiza las acciones necesarias para realizar ese estado (por ejemplo, la eliminación de paquetes antiguos y la instalación de paquetes nuevos).

Ver también:

Probado en Ubuntu 19.10.

Pitón aptPaquete

Hay un paquete de Python 3 preinstalado llamado apt en Ubuntu 18.04 que expone una interfaz de Python apt!

Un script que verifica si un paquete está instalado y lo instala si no se puede ver en: Cómo instalar un paquete usando la API python-apt

Aquí hay una copia para referencia:

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

Compruebe si hay un ejecutable en su PATHlugar

Ver: ¿Cómo puedo verificar si un programa existe desde un script Bash?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
Ciro, no puedes confiar en el código de salida "dpkg -s". Por ejemplo, si "instaló" un paquete, luego "lo eliminó" e intentó "dpkg -s packagename", entonces notará el estado: desinstale y salga del código cero (como si estuviera instalado). Debe analizar el resultado "dpkg -s" bro.
Dmitry Shevkoplyas
@DmitryShevkoplyas gracias por el informe. No podía reproducirse en Ubuntu 19.10 con: sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. ¿Pueden proporcionar más detalles?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
de hecho, para su caso de prueba con el paquete "hola", "dpkg -s" muestra correctamente el paquete como no instalado y proporciona el código de salida esperado "1". Pero intente la misma verificación de instalación / eliminación con el paquete "certbot", luego verá "Estado: desinstale ok config-files" como la salida "dpkg -s" después de que su "apt remove certbot" y el código de salida nos muestren incorrectamente "0". Mi suposición equivocada fue que es el caso exacto para cualquier otro paquete, pero parece que no es lo mismo para todos, lo que es aún peor y menos predecible. Parse "dpkg -s" debes (c) Yoda :)
Dmitry Shevkoplyas
11

Ofrezco esta actualización ya que Ubuntu agregó su "Archivo de paquete personal" (PPA) justo cuando se respondió esta pregunta, y los paquetes de PPA tienen un resultado diferente.

  1. Paquete de repositorio nativo de Debian no instalado:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Paquete PPA registrado en el host e instalado:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Paquete PPA registrado en el host pero no 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.

También publicado en: /superuser/427318/test-if-a-package-is-installed-in-apt/427898

tahoar
fuente
2
Si instala y elimina un paquete, luego usa el paquete dpkg-query; echo $? será 0 también si el paquete no está instalado.
Pol Hallen
8

UpAndAdam escribió:

Sin embargo, no puede simplemente confiar en los códigos de retorno aquí para las secuencias de comandos

En mi experiencia, puede confiar en los códigos de salida de dkpg.

El código de retorno de dpkg -s es 0 si el paquete está instalado y 1 si no lo está, por lo que la solución más simple que encontré fue:

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

Funciona bien para mi ...

rocka84
fuente
11
Después apt-get remove <package>, dpkg -s <package>aún devuelve 0, aunque el paquete esdeinstalled
ThorSummoner
7

Esto parece funcionar bastante bien.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Devuelve 0si no está instalado o algún número > 0si está instalado.
sandman
fuente
8
grep | wc -lEs un antipatrón. Para verificar si algo existe, simplemente desea grep -q. Para contar realmente las ocurrencias (que rara vez es útil en este tipo de escenario), use grep -c.
tripleee
@tripleee Por lo tanto, dpkg -s zip | grep -c "Package: zip"? (usando zip como paquete de muestra)
David Tabernero M.
@Davdriver Eso no es exactamente lo que hace lo anterior, pero sí. En una secuencia de comandos, probablemente desee grep -q 'Package: zip'devolver un código de salida que indique si se encontró o no el resultado sin imprimir nada.
tripleee
esto parece funcionar bien también para paquetes desinstalados
mehmet
4

Me decidí por uno basado en la respuesta 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

Básicamente, el mensaje de error de dpkg --get-selectionses mucho más fácil de analizar que la mayoría de los demás, porque no incluye estados como "desinstalar". También puede verificar múltiples paquetes simultáneamente, algo que no puede hacer con solo códigos de error.

Explicación / ejemplo:

$ 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

Por lo tanto, grep elimina los paquetes instalados de la lista y awk extrae los nombres de los paquetes del mensaje de error, lo MISSING='python3-venv python3-dev jq'que se puede insertar trivialmente en un comando de instalación.

No estoy emitiendo ciegamente apt-get install $PACKAGESporque, como se menciona en los comentarios, esto puede actualizar inesperadamente paquetes que no estaba planeando; No es realmente una buena idea para procesos automatizados que se espera que sean estables.

Izkata
fuente
Me gusta esta solución Conciso y pruebas para múltiples paquetes a la vez. Además, puede hacer que la verificación opcional sea algo tan simple como[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk
3

He encontrado que todas las soluciones anteriores pueden producir un falso positivo si se instala un paquete y luego se elimina, pero el paquete de instalación permanece en el sistema.

Para replicar: Instalar paquete apt-get install curl
Eliminar paqueteapt-get remove curl

Ahora prueba las respuestas anteriores.

El siguiente comando parece resolver esta condición:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Esto dará como resultado una instalación definitiva o no instalada

marca
fuente
no del todo, lamentablemente, otros posibles resultados en este caso lo son config-files, por lo que creo que | grep -q "installed"realmente se necesita una final para obtener un estado de código de salida funcional.
interesado el
hacer eso| grep -q '^installed$'
entusiasta
3

Parece que hoy en día apt-gettiene una opción --no-upgradeque simplemente hace lo que quiere el OP:

--no-upgradeNo actualice paquetes. Cuando se usa junto con la instalación, la no actualización evitará que los paquetes listados se actualicen si ya están instalados.

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

Por lo tanto, puedes usar

apt-get install --no-upgrade package

y packagese instalará solo si no es así.

Giovanni Mascellani
fuente
2

Esto lo hará. apt-get installes idempotente

sudo apt-get install command
David Baucum
fuente
55
Hay escenarios en los que hacer un apt-get installpaquete no es deseable cuando el paquete ya está instalado, aunque el comando en sí sea idempotente. En mi caso, estoy instalando un paquete en un sistema remoto con el módulo sin procesar de Ansible, que informará que el sistema cambiará cada vez que lo ejecute apt-get installindiscriminadamente. Un condicional resuelve ese problema.
JBentley
1
@JBentley Ese es un buen punto. Los paquetes que se instalan como parte de una dependencia se marcarán como instalados manualmente, y luego no se eliminarán cuando se elimine su dependencia si se instala correctamente.
David Baucum
2

Utilizar:

apt-cache policy <package_name>

Si no está instalado, mostrará:

Installed: none

De lo contrario, mostrará:

Installed: version
Mohammed Noureldin
fuente
1

Esta característica ya existe en Ubuntu y Debian, en el command-not-foundpaquete.

camh
fuente
15
matt @ matt-ubuntu: ~ $ command-not-found command-not-found: comando no encontrado ... jajaja.
Matt Fletcher
1
command-not-foundes un ayudante interactivo, no una herramienta para garantizar que tenga las dependencias que desea. Por supuesto, la forma correcta de declarar dependencias es empaquetar su software en un paquete Debian y completar la Depends:declaración en el debian/controlarchivo del paquete correctamente.
tripleee
1
apt list [packagename]

parece ser la forma más sencilla de hacerlo fuera de dpkg y herramientas apt- * anteriores

Erich
fuente
Es bueno para la verificación manual, pero emite una advertencia que indica que apt no está diseñado para secuencias de comandos, en contraste con las herramientas apt- *.
Hontvári Levente
1

Tuve un requisito similar al ejecutar la prueba localmente en lugar de en Docker. Básicamente, solo quería instalar los archivos .deb encontrados si aún no estaban 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

Supongo que el único problema que puedo ver es que no comprueba el número de versión del paquete, por lo que si el archivo .deb es una versión más nueva, esto no sobrescribirá el paquete instalado actualmente.

Craig
fuente
1

Para Ubuntu, apt proporciona una forma bastante decente de hacer esto. A continuación se muestra un ejemplo para Google Chrome:

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

Estoy redirigiendo la salida de error a nulo porque apt advierte contra el uso de su "cli inestable". Sospecho que el paquete de la lista es estable, así que creo que está bien descartar esta advertencia. El -qq hace que sea súper silencioso.

carlin.scott
fuente
1
esto no funcionará correctamente si algo es "actualizable"
Pawel Barcik
@PawelBarcik buen punto. He actualizado la respuesta para manejar esa situación.
carlin.scott
0

Este comando es el más memorable:

dpkg --get-selections <package-name>

Si está instalado, imprime:

<package-name> instalar

De lo contrario, imprime

No se encontraron paquetes que coincidan con <package-name>.

Esto fue probado en Ubuntu 12.04.1 (Precise Pangolin).

iNulty
fuente
44
dpkg --get-selections <package-name>no establece el código de salida en distinto de cero cuando no se encuentra el paquete.
Lucas
0

Se han contado muchas cosas, pero para mí la forma más simple es:

dpkg -l | grep packagename
freedev
fuente
0

En Bash:

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

Tenga en cuenta que puede tener una cadena con varios paquetes en PKG.

daruma
fuente
0

Yo uso la siguiente manera:

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
fuente