Проверьте, установлен ли пакет apt-get, а затем установите его, если он не работает в Linux.

223

Я работаю над системой Ubuntu, и в настоящее время это то, что я делаю:

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

Это то, что большинство людей будет делать? Или есть более элегантное решение?

Джон Цзян
источник
7
Имена команд не всегда отражают имя пакета, к которому они принадлежат. Какова ваша большая цель? Почему бы вам просто не попробовать установить его, а в худшем случае - нет, поскольку он уже установлен.
viam0Zah
8
К счастью, apt-get install идемпотентен, поэтому его можно просто запустить и не беспокоиться о том, установлен он или нет.
Дэвид Баукум
Комментарий @ DavidBaucum должен быть ответом, который наберет наибольшее количество голосов.
Нирмал,
@ Нирмал, ответ сделан.
Дэвид Баукум
1
Связанные, вы должны использовать command -v <command>; не which <command>. Также см. Проверка, существует ли программа из скрипта Bash .
17

Ответы:

314

Чтобы проверить, packagenameбыл ли установлен, введите:

dpkg -s <packagename>

Вы также можете использовать dpkg-queryтот, который имеет более точный вывод для ваших целей, а также принимает групповые символы.

dpkg-query -l <packagename>

Чтобы узнать, какой пакет принадлежит command, попробуйте:

dpkg -S `which <command>`

Для получения дополнительной информации см. Статью Узнайте, установлен ли пакет в Linux и шпаргалке dpkg .

viam0Zah
источник
32
Если вы, как личность, хотите это НЕ программно, вы можете использовать эту информацию в том виде, в каком она есть. Однако вы не можете просто полагаться на коды возврата здесь для сценариев или только на вывод / отсутствие вывода для сценариев. Вам придется сканировать вывод этих команд, ограничивая их полезность для этого вопроса.
UpAndAdam
4
Как ни странно, я недавно обнаружил, что dpkg-query раньше возвращал 1 в отсутствующем пакете, теперь (Ubuntu 12.04) возвращает 0, вызывая всевозможные проблемы в моем скрипте установки узла сборки jenkins! dpkg -s возвращает 0 для установленного пакета и 1 для не установленного.
Therealstubot
18
Эй, ОП попросил ifиспользовать. Я также ищу ifиспользование.
Томаш Зато - Восстановить Монику
1
@Therealstubot: я также использую Ubuntu 12.04 и dpkg -sвозвращаю 1 для отсутствующих пакетов и 0 в противном случае, как и должно быть. Чем отличались предыдущие (или последние) версии?
MestreLion
4
примечание: dpkg -sвозвращает ноль, если пакет был установлен, а затем удален - в этом случае он Status: deinstall ok config-filesили похож, так что все в порядке - так что для меня это не безопасный тест. dpkg-query -lв этом случае, похоже, тоже не возвращает полезного результата.
острому
86

Чтобы быть немного более явным, вот немного bash-скрипта, который проверяет пакет и устанавливает его при необходимости. Конечно, вы можете сделать другие вещи, обнаружив, что пакет отсутствует, например, просто выйдя с кодом ошибки.

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

Если скрипт выполняется в графическом интерфейсе (например, это скрипт Nautilus), вы, вероятно, захотите заменить вызов sudo на вызов gksudo.

Urhixidur
источник
5
--force-yesкажется плохой идеей. Со страницы man: «Это опасный параметр, который заставляет apt-get продолжать работу без запроса, если он делает что-то потенциально опасное. Его не следует использовать, за исключением очень особых ситуаций. Использование --force-yes может потенциально разрушить вашу систему !» Использование его в сценарии делает его еще хуже.
снижение активности
68

Эта однострочная строка возвращает 1 (установлено) или 0 (не установлено) для пакета 'nano'.

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

даже если пакет не существует / недоступен.

Пример ниже устанавливает пакет 'nano', если он не установлен ..

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
Себ
источник
4
Моя вариация на это:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner
@ThorSummoner: хотите объяснить, почему ваш лучше?
knocte
1
@knocte Я не уверен, что есть аргумент о том, чтобы быть объективно лучше. Хотя я уверен, что однострочный пост в стенографическом сообщении будет выполнять вывод результатов, который я не хотел бы оставлять в ответ болтаться. Один вкладыш, который я показываю, иллюстрирует получение (печать) только кода выхода.
ThorSummoner
1
@ThorSummoner Вам не нужно grep -Pтакое простое регулярное выражение.
tripleee
7
Проще: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Не нужно использовать grep -c, просто используйте статус выходаgrep
Стивен Остермиллер
17

dpkg -s программное использование с автоматической установкой

Мне нравится, dpkg -sкак он выходит со статусом, 1если какой-либо из пакетов не установлен, что позволяет легко автоматизировать:

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

man dpkg К сожалению, не документирует статус выхода, но я думаю, что на него следует полагаться в достаточной степени:

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

Стоит отметить, что работает:

sudo apt remove <package-name>

не обязательно сразу удаляет все файлы для некоторых пакетов (но делает для других, не уверен почему?), а просто помечает пакет для удаления.

В этом состоянии пакет, по-видимому, все еще пригоден для использования, и поскольку его файлы все еще присутствуют, но он помечен для удаления позже.

Например, если вы запустите:

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

затем:

  • первые два echo $?выхода 0, только третий выводит1

  • вывод для первого dpkg -s certbotсодержит:

    Status: deinstall ok installed

    в то время как второй говорит:

    Status: deinstall ok config-files

    и он исчезает только после чистки:

    dpkg-query: package 'certbot' is not installed and no information is available
  • файл /etc/logrotate.d/certbotвсе еще присутствует в системе после apt remove, но не после --purge.

    Тем не менее, файл /usr/lib/python3/dist-packages/certbot/reporter.pyвсе еще присутствует даже после --purge.

Я не понимаю, почему, но с helloпакетом второй dpkgпосле apt removeпоказывает, что он уже был удален без --purge:

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

Документация также очень неясна, например:

sudo apt dselect-upgrade

не удаляется, certbotкогда он помечен как deinstall, хотя, man apt-getкажется, указывает, что:

dselect-upgradeиспользуется в сочетании с традиционной оболочкой Debian, dselect (1). dselect-upgrade следует изменениям, внесенным dselect (1) в поле «Состояние» доступных пакетов, и выполняет действия, необходимые для реализации этого состояния (например, удаление старых и установка новых пакетов).

Смотрите также:

Проверено на Ubuntu 19.10.

aptПакет Python

В aptUbuntu 18.04 есть предустановленный пакет Python 3, который предоставляет интерфейс Python apt!

Сценарий, который проверяет, установлен ли пакет, и устанавливает его, если его нет, по адресу: Как установить пакет с помощью API python-apt

Вот копия для справки:

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

Проверьте, находится ли исполняемый файл PATHвместо

Смотрите: Как я могу проверить, существует ли программа из Bash-скрипта?

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
Сиро, ты не можешь положиться на код выхода "dpkg -s". Например, если вы «apt установили» пакет, затем «apt remove» и попытались «dpkg -s packagename», вы увидите статус: деинсталлируйте и выйдите из кода ноль (как если бы он был установлен). Вы должны проанализировать вывод "dpkg -s", братан.
Дмитрий Шевкопляс
@DmitryShevkoplyas спасибо за отчет. Я не мог воспроизвести на Ubuntu 19.10 с: sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Можете ли вы предоставить более подробную информацию?
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
действительно - для вашего тестового примера с пакетом "hello" dpkg -s правильно показывает пакет как не установленный и выдает ожидаемый код выхода "1". Но попробуйте ту же самую проверку установки / удаления с пакетом «certbot», тогда вы увидите «Status: deinstall ok config-files», так как вывод «dpkg -s» после вашего «apt remove certbot» и кода выхода неверно показывает нам "0". Мое неверное предположение состояло в том, что это точный случай для любого другого пакета, но кажется, что это не то же самое для всех, что еще хуже и менее предсказуемо. Разобрать "dpkg -s" надо (с) Йода :)
Дмитрий Шевкопляс
11

Я предлагаю это обновление, так как Ubuntu добавил свой «Личный архив пакетов» (PPA), как только на этот вопрос был дан ответ, и пакеты PPA имеют другой результат.

  1. Собственный пакет репозитория Debian не установлен:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Пакет PPA зарегистрирован на хосте и установлен:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Пакет PPA зарегистрирован на хосте, но не установлен:

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

Также размещено по адресу : /superuser/427318/test-if-a-package-is-installed-in-apt/427898

tahoar
источник
2
Если вы устанавливаете и удаляете пакет, далее вы используете пакет dpkg-query; echo $? также будет 0, если пакет не установлен.
Пол Халлен
8

UpAndAdam написал:

Однако вы не можете просто полагаться на коды возврата здесь для сценариев

По моему опыту вы можете положиться на коды выхода dkpg.

Код возврата dpkg -s равен 0, если пакет установлен, и 1, если нет, поэтому самое простое решение, которое я нашел, было:

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

У меня отлично работает ...

rocka84
источник
11
После того apt-get remove <package>, как dpkg -s <package>все еще возвращает 0, хотя пакетdeinstalled
ThorSummoner
7

Кажется, это работает очень хорошо.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Он либо возвращает, 0если не установлен, либо какой-либо номер, > 0если установлен.
Дрема
источник
8
grep | wc -lэто антипаттерн. Чтобы проверить, если что-то существует, вы хотите просто grep -q. Для фактического подсчета вхождений (что редко бывает полезно в подобных ситуациях) используйте grep -c.
tripleee
@tripleee Так dpkg -s zip | grep -c "Package: zip"? (с использованием почтового индекса в качестве образца пакета)
Дэвид Табернеро М.
@Davdriver Это не совсем то, что сделано выше, но да. В сценарии вы, вероятно, захотите grep -q 'Package: zip'вернуть код завершения, который указывает, был ли найден результат без печати чего-либо.
tripleee
кажется, это работает нормально и для неустановленных пакетов
mehmet
4

Я остановился на одном, основываясь на ответе Нулти :

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

По сути, сообщение об ошибке от dpkg --get-selectionsнамного легче разобрать, чем большинство других, потому что оно не включает такие состояния, как «деинсталляция». Он также может проверять несколько пакетов одновременно, чего нельзя сделать только с помощью кодов ошибок.

Объяснение / пример:

$ 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

Поэтому grep удаляет установленные пакеты из списка, а awk извлекает имена пакетов из сообщения об ошибке, в результате MISSING='python3-venv python3-dev jq'чего их можно легко вставить в команду установки.

Я не выпускаю вслепую, apt-get install $PACKAGESпотому что, как упоминалось в комментариях, это может неожиданно обновить пакеты, которые вы не планировали; не очень хорошая идея для автоматизированных процессов, которые, как ожидается, будут стабильными.

Izkata
источник
Мне нравится это решение. Краткий и тесты для нескольких пакетов одновременно. Кроме того, вы можете сделать дополнительную проверку чего-то столь же простого, как[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Шенк
3

Я обнаружил, что все приведенные выше решения могут привести к ложному срабатыванию, если пакет установлен, а затем удален, но установочный пакет остается в системе.

Для репликации: Установить пакет apt-get install curl
Удалить пакетapt-get remove curl

Теперь проверьте приведенные выше ответы.

Следующая команда, кажется, решает это условие:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Это приведет к окончательной установке или неустановке

отметка
источник
не совсем, к сожалению - другие возможные результаты в этом случае таковы, config-filesтак что я думаю, что окончательный вариант | grep -q "installed"действительно необходим для получения функционального кода выхода.
острому
сделать это| grep -q '^installed$'
увлеченно
3

Кажется, что в настоящее время apt-getесть опция, --no-upgradeкоторая делает то, что хочет ОП:

--no-upgradeНе обновляйте пакеты. При использовании вместе с установкой no-upgrade не позволит обновить перечисленные пакеты, если они уже установлены.

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

Поэтому вы можете использовать

apt-get install --no-upgrade package

и packageбудет установлен, только если это не так.

Джованни Масцеллани
источник
2

Это сделает это. apt-get installидемпотент.

sudo apt-get install command
Дэвид Баукум
источник
5
Существуют сценарии, в которых выполнение apt-get installпакета on нежелательно, когда пакет уже установлен, хотя сама команда является идемпотентной. В моем случае я устанавливаю пакет в удаленной системе с необработанным модулем Ansible, который будет сообщать об изменении системы каждый раз, когда я буду работать apt-get installбез разбора. Условное решение этой проблемы.
Дж. Бентли,
1
@JBentley Это хороший момент. Пакеты, установленные как часть зависимости, будут помечены как установленные вручную, а затем не будут удалены при удалении зависимости, если вы установили apt-get.
Дэвид Баукум
2

Использование:

apt-cache policy <package_name>

Если он не установлен, он покажет:

Installed: none

Иначе это покажет:

Installed: version
Мухаммед Нурельдин
источник
1

Эта функция уже существует в Ubuntu и Debian в command-not-foundпакете.

CAMH
источник
15
matt @ matt-ubuntu: ~ $ command-not-found команда-not-found: команда не найдена ... смеется.
Мэтт Флетчер
1
command-not-foundэто интерактивный помощник, а не инструмент, который гарантирует, что у вас есть зависимости, которые вы хотите. Конечно, правильным способом объявления зависимостей является упаковка вашего программного обеспечения в пакет Debian и заполнение Depends:декларации в пакете.debian/control правильно файле .
tripleee
1
apt list [packagename]

кажется, самый простой способ сделать это вне dpkg и старых инструментов apt- *

Erich
источник
Это хорошо для ручной проверки, но выдает предупреждение о том, что apt не предназначен для сценариев - в отличие от инструментов apt- *.
Хонтвари Левенте
1

У меня было подобное требование при запуске теста локально, а не в докере. По сути, я хотел установить только те файлы .deb, которые не были установлены.

# 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

Я думаю, что единственная проблема, которую я вижу, заключается в том, что он не проверяет номер версии пакета, поэтому, если файл .deb является более новой версией, это не перезапишет установленный в данный момент пакет.

Craig
источник
1

Для Ubuntu apt предоставляет достаточно приличный способ сделать это. Ниже приведен пример для Google Chrome:

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

Я перенаправляю вывод ошибок на ноль, потому что apt предупреждает против использования своего "нестабильного кли". Я подозреваю, что список пакетов стабилен, поэтому я думаю, что можно выбросить это предупреждение. -Qq делает супер тихий.

carlin.scott
источник
1
это не будет работать должным образом, если что-то «модернизируемое»
Pawel Barcik
@PawelBarcik хорошая мысль. Я обновил ответ, чтобы справиться с этой ситуацией.
carlin.scott
0

Эта команда самая запоминающаяся:

dpkg --get-selections <package-name>

Если он установлен, он печатает:

<имя-пакета> установить

В противном случае он печатает

Не найдено пакетов, соответствующих <имя-пакета>.

Это было проверено на Ubuntu 12.04.1 (Precise Pangolin).

iNulty
источник
4
dpkg --get-selections <package-name>не устанавливает код выхода, отличный от нуля, когда пакет не найден.
Лукас
0

Многое было сказано, но для меня самый простой способ это:

dpkg -l | grep packagename
freedev
источник
0

В Баше:

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

Обратите внимание, что в PKG может быть строка с несколькими пакетами.

Дарума
источник
0

Я использую следующий способ:

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
Нитиш Джадия
источник