Kiểm tra xem gói apt-get đã được cài đặt chưa và sau đó cài đặt nó nếu nó không có trên Linux


223

Tôi đang làm việc trên một hệ thống Ubuntu và hiện tại đây là những gì tôi đang làm:

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

Đây có phải là những gì hầu hết mọi người sẽ làm? Hoặc có một giải pháp thanh lịch hơn?


7
Tên lệnh không phải lúc nào cũng phản ánh tên gói mà chúng thuộc về. Mục tiêu lớn hơn của bạn là gì? Tại sao bạn không đơn giản thử cài đặt nó, và trường hợp xấu nhất là nó sẽ không được, vì nó đã được cài đặt.
viam0Zah

8
May mắn thay, apt-get install là idempotent, vì vậy an toàn khi chỉ chạy nó và không lo lắng về việc nó có được cài đặt hay không.
David Baucum

Nhận xét của DavidBaucum nên là một câu trả lời sẽ nhận được nhiều phiếu bầu nhất.
Nertal

@Nertal, câu trả lời được thực hiện.
David Baucum

1
Liên quan, bạn nên sử dụng command -v <command>; không which <command>. Cũng xem Kiểm tra nếu một chương trình tồn tại từ một tập lệnh Bash .
jww

Câu trả lời:


314

Để kiểm tra xem packagenameđã được cài đặt chưa, gõ:

dpkg -s <packagename>

Bạn cũng có thể sử dụng dpkg-querycó đầu ra gọn gàng hơn cho mục đích của mình và cũng chấp nhận thẻ hoang dã.

dpkg-query -l <packagename>

Để tìm gói nào sở hữu command, hãy thử:

dpkg -S `which <command>`

Để biết thêm chi tiết, xem bài viết Tìm hiểu xem gói được cài đặt trong Linuxdpkg cheat sheet .


32
Nếu bạn là một người muốn điều này KHÔNG lập trình, bạn có thể sử dụng thông tin này khi nó đứng. Tuy nhiên, bạn không thể chỉ dựa vào mã trả lại ở đây để tạo kịch bản hoặc chỉ xuất / thiếu đầu ra cho kịch bản. Bạn sẽ phải quét đầu ra của các lệnh này, hạn chế tính hữu dụng của chúng cho câu hỏi này.
UpAndAdam

4
Thật kỳ lạ, gần đây tôi đã phát hiện ra rằng truy vấn dpkg được sử dụng để trả về 1 trên gói bị thiếu, bây giờ (Ubuntu 12.04) trả về 0, gây ra tất cả các loại rắc rối trên tập lệnh thiết lập nút xây dựng jenkins của tôi! dpkg -s trả về 0 trên gói đã cài đặt và 1 trên gói không được cài đặt.
Therealstubot

18
Này, OP yêu cầu ifsử dụng. Tôi cũng đang tìm kiếm ifcách sử dụng.
Tomáš Zato - Phục hồi Monica

1
@Therealstubot: Tôi cũng đang sử dụng Ubuntu 12.04 và dpkg -strả về 1 cho các gói bị thiếu và 0 nếu không. Làm thế nào khác nhau trên các phiên bản trước (hoặc gần đây)?
MestreLion

4
một lưu ý: dpkg -strả về 0 nếu gói được cài đặt và sau đó bị xóa - trong trường hợp đó là Status: deinstall ok config-filestương tự, vì vậy nó "ok" - vì vậy với tôi, đây không phải là một thử nghiệm an toàn. dpkg-query -ldường như không trả về một kết quả hữu ích trong trường hợp này.
quan tâm

86

Nói rõ hơn một chút, đây là một đoạn script bash kiểm tra gói và cài đặt nó nếu cần. Tất nhiên, bạn có thể làm những việc khác khi phát hiện ra rằng gói bị thiếu, chẳng hạn như thoát ra với mã lỗi.

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

Nếu tập lệnh chạy trong GUI (ví dụ: tập lệnh Nautilus), có lẽ bạn sẽ muốn thay thế lời gọi 'sudo' bằng một 'gksudo'.


5
--force-yescó vẻ là một ý tưởng kém Từ trang man: "Đây là một tùy chọn nguy hiểm sẽ khiến apt-get tiếp tục mà không cần nhắc nếu nó đang làm gì đó có hại. Nó không nên được sử dụng trừ những trường hợp rất đặc biệt. Sử dụng - Force-yes có thể phá hủy hệ thống của bạn ! " Sử dụng nó trong một kịch bản làm cho nó thậm chí còn tồi tệ hơn.
giảm hoạt động

68

Lớp lót này trả về 1 (đã cài đặt) hoặc 0 (chưa cài đặt) cho gói 'nano' ..

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

ngay cả khi gói không tồn tại / không có sẵn.

Ví dụ dưới đây cài đặt gói 'nano' nếu nó chưa được cài đặt ..

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

4
Biến thể của tôi về điều này:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner

@ThorSummoner: quan tâm giải thích tại sao bạn tốt hơn?
knocte

1
@knocte Tôi không chắc có một lập luận được đưa ra về việc khách quan hơn. Mặc dù tôi tự tin rằng một bài viết nguyên văn của bài đăng sẽ thực hiện kết quả đầu ra, điều mà tôi không muốn để lại lủng lẳng trong câu trả lời. Một lớp lót tôi hiển thị minh họa việc nhận (in) chỉ mã thoát.
ThorSummoner

1
@ThorSummoner Bạn không cần grep -Pmột regex đơn giản như thế.
tripleee

7
Đơn giản hơn: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi- Không cần sử dụng grep -c, chỉ cần sử dụng trạng thái thoát củagrep
Stephen Ostermiller

17

dpkg -s sử dụng chương trình với cài đặt tự động

Tôi thích dpkg -svì nó thoát với trạng thái 1nếu bất kỳ gói nào không được cài đặt, giúp dễ dàng tự động hóa:

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

man dpkg không may ghi lại trạng thái thoát, nhưng tôi nghĩ nó nên an toàn một cách hợp lý để dựa vào nó:

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

Một điều cần lưu ý là chạy:

sudo apt remove <package-name>

không nhất thiết phải xóa tất cả các tệp ngay lập tức đối với một số gói (nhưng đối với các gói khác, không biết tại sao?) và chỉ đánh dấu gói để xóa.

Ở trạng thái này, gói dường như vẫn có thể sử dụng được và vì các tệp của nó vẫn còn, nhưng nó được đánh dấu để xóa sau này.

Ví dụ: nếu bạn chạy:

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

sau đó:

  • hai echo $?đầu ra đầu tiên 0, chỉ đầu ra thứ ba1

  • đầu ra cho lần đầu tiên dpkg -s certbotchứa:

    Status: deinstall ok installed

    trong khi người thứ hai nói:

    Status: deinstall ok config-files

    và nó chỉ biến mất sau khi thanh trừng:

    dpkg-query: package 'certbot' is not installed and no information is available
  • tập tin /etc/logrotate.d/certbotvẫn còn trong hệ thống sau apt remove, nhưng không phải sau đó --purge.

    Tuy nhiên, tập tin /usr/lib/python3/dist-packages/certbot/reporter.pyvẫn có mặt ngay cả sau đó --purge.

Tôi không hiểu tại sao, nhưng với hellogói thứ hai dpkgsau apt removeđó cho thấy gói đó đã bị xóa mà không có --purge:

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

Các tài liệu cũng rất không rõ ràng, ví dụ:

sudo apt dselect-upgrade

đã không xóa certbotkhi được đánh dấu là deinstall, mặc dù man apt-getdường như chỉ ra rằng:

dselect-upgradeđược sử dụng cùng với giao diện đóng gói Debian truyền thống, dselect (1). nâng cấp DSelect theo các thay đổi được thực hiện bởi dselect (1) đối với trường Trạng thái của các gói có sẵn và thực hiện các hành động cần thiết để nhận ra trạng thái đó (ví dụ: loại bỏ gói cũ và cài đặt gói mới).

Xem thêm:

Đã thử nghiệm trên Ubuntu 19.10.

aptGói Python

Có một gói Python 3 được cài đặt sẵn có tên apttrong Ubuntu 18.04, cho thấy giao diện Python apt!

Tập lệnh kiểm tra xem một gói đã được cài đặt và cài đặt chưa nếu không thể xem tại: Cách cài đặt gói bằng API python-apt

Đây là một bản sao để tham khảo:

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

Kiểm tra xem một thực thi là trong PATHthay thế

Xem: Làm cách nào để kiểm tra xem chương trình có tồn tại từ tập lệnh Bash không?


Ciro, ou không thể dựa vào mã thoát "dpkg -s". Ví dụ: nếu bạn "apt đã cài đặt" một gói, sau đó "apt remove" nó và cố gắng "dpkg -s packagename" thì bạn sẽ nhận thấy trạng thái: hủy cài đặt và thoát mã 0 (như thể đã cài đặt). Bạn phải phân tích cú pháp đầu ra "dpkg -s".
Dmitry Shevkoplyas

@DmitryShevkoplyas cảm ơn vì báo cáo. Tôi không thể sao chép trên Ubuntu 19.10 với : sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?. Bạn có thể cung cấp thêm chi tiết?
Ciro Santilli 郝海东 冠状 病 事件

1
thực sự - đối với trường hợp thử nghiệm của bạn với gói "xin chào" "dpkg -s" hiển thị chính xác gói không được cài đặt và cung cấp mã thoát dự kiến ​​"1". Nhưng hãy thử kiểm tra instal / remove tương tự với gói "certbot", sau đó bạn sẽ thấy "Trạng thái: deinstall ok config-files" là đầu ra "dpkg -s" sau khi "apt remove certbot" và mã thoát không chính xác cho chúng tôi thấy "0". Giả định sai của tôi là trường hợp chính xác cho bất kỳ gói nào khác, nhưng có vẻ như nó không giống nhau cho tất cả, điều này thậm chí còn tồi tệ hơn và ít dự đoán hơn. Phân tích "dpkg -s" bạn phải (c) Yoda :)
Dmitry Shevkoplyas

11

Tôi cung cấp bản cập nhật này vì Ubuntu đã thêm "Lưu trữ gói cá nhân" (PPA) giống như câu hỏi này đã được trả lời và các gói PPA có kết quả khác.

  1. Gói kho lưu trữ Debian gốc chưa được cài đặt:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
  2. Gói PPA đã đăng ký trên máy chủ và được cài đặt:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
  3. Gói PPA đã đăng ký trên máy chủ nhưng chưa được cài đặt:

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

Cũng được đăng trên: /superuser/427318/test-if-a-package-is-installed-in-apt/427898


2
Nếu bạn cài đặt và xóa gói, tiếp theo bạn sử dụng gói truy vấn dpkg; tiếng vang $? cũng sẽ là 0 nếu gói không được cài đặt.
Pol Hallen

8

UpAndAdam đã viết:

Tuy nhiên, bạn không thể đơn giản dựa vào mã trả lại ở đây để viết kịch bản

Theo kinh nghiệm của tôi, bạn có thể dựa vào mã thoát của dkpg.

Mã trả về của dpkg -s0 nếu gói được cài đặt và 1 nếu không, vì vậy giải pháp đơn giản nhất tôi tìm thấy là:

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

Hoạt động tốt với tôi ...


11
Sau đó apt-get remove <package>, dpkg -s <package>vẫn trả về 0, mặc dù gói làdeinstalled
ThorSummoner

7

Điều này dường như làm việc khá tốt.

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • Nó sẽ trả về 0nếu không được cài đặt hoặc một số số > 0nếu được cài đặt.

8
grep | wc -llà một antipotype. Để kiểm tra nếu một cái gì đó tồn tại, bạn muốn đơn giản grep -q. Để thực sự đếm số lần xuất hiện (điều này hiếm khi hữu ích trong loại kịch bản này), hãy sử dụng grep -c.
tripleee

@tripleee Vậy , dpkg -s zip | grep -c "Package: zip"? (sử dụng zip làm gói mẫu)
David Tabernero M.

@Davdo Đó không phải là chính xác những gì ở trên nhưng có. Trong một tập lệnh, bạn có thể muốn grep -q 'Package: zip'trả về mã thoát cho biết liệu kết quả có được tìm thấy hay không mà không in bất cứ điều gì.
tripleee

điều này dường như cũng hoạt động tốt đối với các gói đã gỡ cài đặt
mehmet

4

Tôi đã giải quyết dựa trên câu trả lời của 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

Về cơ bản, thông báo lỗi từ dpkg --get-selectionsphân tích dễ dàng hơn nhiều so với hầu hết các thông báo khác, bởi vì nó không bao gồm các trạng thái như "deinstall". Nó cũng có thể kiểm tra đồng thời nhiều gói, một số thứ bạn không thể làm chỉ với mã lỗi.

Giải thích / ví dụ:

$ 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

Vì vậy, grep loại bỏ các gói đã cài đặt khỏi danh sách và awk lấy tên gói ra khỏi thông báo lỗi, dẫn đến MISSING='python3-venv python3-dev jq', có thể được chèn một cách tầm thường vào lệnh cài đặt.

Tôi không mù quáng ban hành apt-get install $PACKAGESvì như đã đề cập trong các bình luận, điều này có thể bất ngờ nâng cấp các gói bạn không dự định thực hiện; không thực sự là một ý tưởng tốt cho các quy trình tự động được dự kiến ​​sẽ ổn định.


Tôi thích giải pháp này. Súc tích và kiểm tra cho nhiều gói cùng một lúc Ngoài ra, bạn có thể thực hiện kiểm tra tùy chọn một cái gì đó đơn giản như[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk

3

Tôi đã tìm thấy tất cả các giải pháp ở trên có thể tạo ra dương tính giả nếu gói được cài đặt và sau đó gỡ bỏ nhưng gói cài đặt vẫn còn trên hệ thống.

Để nhân rộng: Cài đặt gói apt-get install curl
Xóa góiapt-get remove curl

Bây giờ kiểm tra câu trả lời trên.

Lệnh sau dường như để giải quyết điều kiện này:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

Điều này sẽ dẫn đến một cài đặt dứt khoát hoặc không được cài đặt


không hoàn toàn, thật đáng buồn - kết quả có thể khác trong trường hợp này là config-files- vì vậy tôi nghĩ rằng một trận chung kết | grep -q "installed"là thực sự cần thiết để có được trạng thái mã thoát chức năng.
quan tâm

làm điều đó| grep -q '^installed$'
quan tâm

3

Dường như ngày nay apt-getcó một tùy chọn --no-upgradechỉ thực hiện những gì OP muốn:

--no-upgradeKhông nâng cấp gói. Khi được sử dụng cùng với cài đặt, không nâng cấp sẽ ngăn các gói được liệt kê khỏi nâng cấp nếu chúng đã được cài đặt.

Trang web từ https://linux.die.net/man/8/apt-get

Do đó bạn có thể sử dụng

apt-get install --no-upgrade package

packagesẽ chỉ được cài đặt nếu không.



2

Điều này sẽ làm điều đó. apt-get installlà bình thường.

sudo apt-get install command

5
Có các kịch bản trong đó thực hiện apt-get installmột gói trên là không mong muốn trong đó gói đã được cài đặt, mặc dù chính lệnh đó là idempotent. Trong trường hợp của tôi, tôi đang cài đặt một gói trên một hệ thống từ xa với mô-đun thô của Ansible, sẽ báo cáo hệ thống thay đổi mỗi lần nếu tôi chạy apt-get installbừa bãi. Một điều kiện giải quyết vấn đề đó.
JBentley

1
@JBentley Đó là một điểm tốt. Các gói được cài đặt như một phần của phần phụ thuộc sẽ được đánh dấu là đã cài đặt thủ công và sau đó sẽ không bị xóa khi phần phụ thuộc bị xóa nếu bạn cài đặt nó.
David Baucum

2

Sử dụng:

apt-cache policy <package_name>

Nếu nó không được cài đặt, nó sẽ hiển thị:

Installed: none

Nếu không, nó sẽ hiển thị:

Installed: version

1

Tính năng này đã tồn tại trong Ubuntu và Debian, trong command-not-foundgói.


15
matt @ matt-ubfox: ~ $ lệnh-not-find lệnh-not-find: lệnh không tìm thấy ... lol.
Matt Fletcher

1
command-not-foundlà một người trợ giúp tương tác, không phải là một công cụ để đảm bảo bạn có các phụ thuộc mà bạn muốn. Tất nhiên, cách thích hợp để khai báo các phụ thuộc là đóng gói phần mềm của bạn trong gói Debian và điền vào phần Depends:khai báo trong debian/controltệp của gói đúng cách.
tripleee

1
apt list [packagename]

dường như là cách đơn giản nhất để thực hiện ngoài dpkg và các công cụ apt- * cũ hơn


Thật tốt khi kiểm tra thủ công, nhưng nó đưa ra một cảnh báo cho biết apt không dành cho kịch bản - trái ngược với các công cụ apt- *.
Hontvári Levente


1

Tôi có một yêu cầu tương tự khi chạy thử cục bộ thay vì trong docker. Về cơ bản tôi chỉ muốn cài đặt bất kỳ tệp .deb nào được tìm thấy nếu chúng chưa được cài đặt.

# 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

Tôi đoán vấn đề duy nhất mà tôi có thể thấy là nó không kiểm tra số phiên bản của gói nên nếu tệp .deb là phiên bản mới hơn, thì điều này sẽ không ghi đè lên gói hiện được cài đặt.


1

Đối với Ubuntu, apt cung cấp một cách khá hợp lý để làm điều này. Dưới đây là một ví dụ cho google chrome:

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

Tôi đang chuyển hướng đầu ra lỗi thành null vì apt cảnh báo không sử dụng "cli không ổn định" của nó. Tôi nghi ngờ gói danh sách là ổn định vì vậy tôi nghĩ rằng nó ổn để ném cảnh báo này đi. Các -qq làm cho apt siêu yên tĩnh.


1
điều này sẽ không hoạt động đúng nếu một cái gì đó là "có thể nâng cấp"
Pawel Barcik

@PawelBarcik điểm tốt. Tôi đã cập nhật câu trả lời để xử lý tình huống đó.
carlin.scott

0

Lệnh này là đáng nhớ nhất:

dpkg --get-selections <package-name>

Nếu nó được cài đặt, nó sẽ in:

<tên gói> cài đặt

Nếu không thì nó in

Không tìm thấy gói nào khớp với <tên gói>.

Điều này đã được thử nghiệm trên Ubuntu 12.04.1 (Pangolin chính xác).


4
dpkg --get-selections <package-name>không đặt mã thoát thành khác không khi không tìm thấy gói.
Lucas

0

Nhiều điều đã được nói nhưng đối với tôi cách đơn giản nhất là:

dpkg -l | grep packagename

0

Trong Bash:

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

Lưu ý rằng bạn có thể có một chuỗi với một số gói trong PKG.


0

Tôi sử dụng cách sau:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.