如何列出手动安装的软件包?

我想获取通过apt或aptitude手动安装的软件包列表,并能够确定foobar软件包是手动安装还是自动安装。
我们如何通过命令行实现这个功能?

1请参考unix.stackexchange.com上的这个答案,它提供了一个过滤掉股票软件包的解决方案。 - Dirk Bergstrom
真的很好的解决方案,可以排除默认安装的软件包:Ubuntu列出明确安装的软件包 - pcworld
包是手动或自动安装的。手动安装还是通过依赖关系安装都可以。我刚刚在WSL上进行了Debian的清洁安装,然后进行了apt更新,并自定义安装了5个软件包:ack,zsh,vim,tree,git。下面有三种得票最高的解决方案列出了112个项目的列表。 - xtian
20个回答

你可以使用以下两个命令行。在我的机器上,它们产生的输出完全相同,并且比此问题中截至目前(2014年7月6日)提出的所有解决方案更精确。 使用apt-mark命令:
comm -23 <(apt-mark showmanual | sort -u) <(gzip -dc /var/log/installer/initial-status.gz | sed -n 's/^Package: //p' | sort -u)

使用 aptitude

comm -23 <(aptitude search '~i !~M' -F '%p' | sed "s/ *$//" | sort -u) <(gzip -dc /var/log/installer/initial-status.gz | sed -n 's/^Package: //p' | sort -u)

非常少量的软件包仍然会有些问题,尽管我怀疑这些实际上是由用户安装的,要么是在语言本地化设置完成后立即安装,要么是通过Totem解码器安装程序等方式。此外,Linux内核头文件版本似乎也会累积,尽管我只安装了非特定版本的元软件包。举个例子:
libreoffice-help-en-gb
openoffice.org-hyphenation
gstreamer0.10-fluendo-mp3
linux-headers-3.13.0-29    

它是如何工作的:

  1. 获取手动安装的软件包列表。对于aptitude,额外使用sed命令去除行末的空白字符。
  2. 获取在全新安装后立即安装的软件包列表。
  3. 比较这两个文件,只输出在文件1中存在但在文件2中不存在的行。

其他可能性效果不佳:

  • 使用ubuntu-14.04-desktop-amd64.manifest文件(Ubuntu 14.04的链接)代替/var/log/installer/initial-status.gz。尽管它们并非如此,但会显示更多手动安装的软件包。
  • 使用apt-mark showauto代替/var/log/installer/initial-status.gz。例如,apt-mark不包括xserver-xorg软件包,而另一个文件包括。

我参考了其他一些StackExchange帖子,但没有一个能像上述解决方案那样有效。

这两个清单列出的软件包比上述解决方案多。

编辑:如果您从之前的版本升级:

如果您将Ubuntu从一个版本升级到另一个版本,您可能需要调整此过程。在这种情况下,我建议除了当前版本的初始状态.gz文件外,还要检查较新版本的清单文件(参见上文)。您可以通过添加另一个比较来轻松完成。仅使用清单文件将无法正常工作,因为不幸的是,清单文件不包含初始状态.gz文件中的所有内容(我已经检查过了)。


12这对我来说不起作用,因为/var/log/installer/initial-status.gz文件丢失了。另外,我想知道这是否取决于apt标记为“manual”还是其他原因? - Anwar
1哎呀,服务器版本没有清单。 - Antti Haapala
我运行了showmanual命令(如下所示)。然后使用comm命令比较了这两个(排序后的)列表。showmanual的结果给出了1840个更多的独特软件包,这些软件包在通过这种方法显示的apt-mark showmanual中没有显示出来。没有任何软件包是唯一存在于这个comm命令的输出中的。对于我的电脑来说,有894个软件包同时出现在两个结果中,我认为这更有趣。不确定为什么会有如此巨大的差异。一些(很多?)软件包似乎是特定于某个版本的。其他像XOrg、GTK组件和lib*之类的东西可能是更新。总之,这个答案是一个非常好的起点。 - will
我刚刚将你的解决方案与apt-mark showmanual进行了比较。有趣的是,可以看到有很多差异。你的列表中有238个软件包,而showmanual返回1717个软件包。在2179个已安装的软件包中,只有223个同时出现在两个列表中,15个只出现在你的列表中(例如:nodejs,lightdm),223个只出现在showmanual中(例如:xserver-xorg,ubuntu-desktop)。感觉你的列表更有帮助,但不知道这些差异来自哪里,很难做出决定...(但我非常确定我手动安装了nginx和lightdm...)[抱歉,我刚才写了同样的内容 ;)] - Daniel Alder
有没有一种方法可以获得我安装的软件包的简明列表?我希望列出大约30个项目的清单,但它充满了像1)我从未听说过的东西(如zenity),2)我听说过但认为是核心功能的东西(如acpi-support),3)对我来说完全无用的东西(如十个printer-driver-xxx,而我甚至没有打印机),以及4)几乎肯定是由另一个应用程序而不是我安装的东西(如thunderbird-locale-en)。 - Mark Jeronimus
2清单文件可以从http://releases.ubuntu.com/下载。 - darkdragon
1这个答案是我到目前为止找到的最好的。然而,它(仍然)缺少临时软件包的建议和推荐。例如,当我使用 apt install git-review 命令时,只有 git-review 被列出。到目前为止,一切都很好。但是当我使用 apt remove git-review 命令时,git 软件包仍然存在但没有被列出。这是因为 git 是软件包 libdpkg-perl 中的一个“建议”(请参阅 /var/lib/dpkg/status)。如果一个软件包是通过 apt install --no-install recommends 命令安装的,并且后来另一个软件包安装了该建议,那么该建议将保留但不会被列出。我不知道如何解决这个问题。 - Alexander Traud
2我最初安装了一个较旧的Ubuntu版本,现在我正在运行20.04版本,所以我从http://releases.ubuntu.com/20.04/ubuntu-20.04.2.0-desktop-amd64.manifest下载了清单文件,然后我运行了`comm -23 <(apt-mark showmanual | sort -u) <(cat ~/Desktop/ubuntu-20.04.2.0-desktop-amd64.manifest | awk '{print $1}' | sort -u)`。谢谢! - bmaupin
1@AlexanderTraud:不确定你是否“想要”捕捉那些。git-review是你手动安装的软件包。而git则不是,无论是建议还是依赖关系。 - MestreLion
@MarkJeronimus:很可能它们是由你的桌面环境代表你安装的,当你确认了一个请求去_"安装支持..."_(特别是在操作语言支持的图形界面时安装*-locale-*)。它们不能被标记为自动安装,否则apt autoremove会将它们删除,所以你的桌面环境会将它们安装为手动安装。 - MestreLion
哇,这真是太棒了!可能不是完全准确,但我认为这是我们能得到的最接近的。我已经将其作为~/.bash_aliases中的apt-list-manual函数实现了,还按照@bmaupin的建议处理了下载的清单文件。 - MestreLion
@MestreLion 当 git-review 仍然安装时,我不需要列出 git。但是,当我移除 git-review 后,我想知道是否有剩余的软件包,例如 git。在这种情况下,尽管它只是一个建议性的软件包,但 git 仍然被标记为已安装。有多个软件包会使 git 保持安装状态。举个例子:我使用一个应用程序,不再想使用它,并且想要删除与之相关的所有已安装内容。现在,有人可能会说这不是最初的问题。然而,在提问时可能并不知道这样的过渡情况。 - Alexander Traud
是的,我们每个人对此的意图可能与原帖的不同,尽管我想一个常见的情况是在安装新版本后能够重新创建系统。在这种情况下,无论是你的情况还是我的情况,我都看不出为什么将git列为手动安装的软件包会有用。如果没有其他人建议或依赖它,apt autoremove可以处理它。 - MestreLion
1@AlexanderTraud:如果您使用此命令的目的是“删除所有_您的_手动‘干预’,使系统‘好像’刚安装和更新”,那么您可以使用mapfile -t mypackages < <(this answer); sudo apt purge "${mypackages[@]}" && sudo apt autoremove - MestreLion
@MestreLion 由其他应用程序安装 ... 由[我的]桌面环境安装。是的,是由另一个应用程序(桌面环境)安装的,而不是我自己(使用apt)。 - Mark Jeronimus
autoremove在我的情况下并不适用,因为git是一个完全不同的、默认安装的软件包的建议项。因此,当任何东西作为副作用安装了git时,git会“粘”在我的系统上。因此,我的使用情况是关于那些列在“将安装以下新软件包”中的软件包:git-review git git-man liberror-perl。我认为这些是手动安装的。apt只考虑git-review是手动安装的,这是可以接受的。然而,即使我删除了git-review,git仍然保持安装状态,并且被标记为“自动”。 - Alexander Traud
1@AlexanderTraud:但是apt做得很对:git-review确实是你要安装的唯一一个软件包,其他的都是作为依赖关系/建议而自动安装的。如果将git标记为手动安装,就意味着没有任何软件会被标记为自动安装,这样auto/manual的概念就完全失去了意义。git之所以保留下来,是因为有其他人现在需要/建议它,但它仍然是一个自动安装的软件包。你从未直接请求过它。 - MestreLion
1而且autoremove会照顾到你的情况,通过删除任何未被你直接请求的“不必要”的软件包。git现在不再是不需要的,有其他人需要/建议它,所以autoremove不应该将其移除。它仍然是auto(正如它应该的那样),只有在没有其他人依赖它时,才能自动删除。 - MestreLion
1@AlexanderTraud 请参考 https://askubuntu.com/questions/351085,这与我的答案提供的不同。在上面的答案中,如果您只安装“git-review”,则不希望“git”出现在手动安装软件包列表中,除非您在某个时候明确输入“apt-get install git”。一旦您安装了“git”,apt-get 将打印“git set to manually installed.” 并将其标记为手动。此答案解决的主要问题是 Ubuntu 自动安装某些软件包,但由于安装方式的原因而标记为手动,这些软件包被过滤掉。 - jmiserez
问题是什么是“手动”安装。我之前手动安装了git,因为我安装了git-review,这是一个副作用。这是事实。由于各种原因,Apt将其标记为“自动”。现在,当我移除git-review时,git会作为一个副作用留在我的系统中,因为它是另一个软件包推荐/建议的。后者的副作用对我来说并不明显。我喜欢你的解决方案,但它有一个不明显的副作用。实际上,我花了相当长的时间才理解为什么有些软件包会“卡住”。这是因为这种传递关系。 - Alexander Traud
@MestreLion,问题正是你提到的“不再需要”。在一个纯净的最小化Ubuntu上,安装git-review,然后移除git-review,git就会卡住。这不是因为后来安装的软件包“需要”git,而是因为一个核心软件包建议使用git。这很难理解。我并不想移除所有建议安装的软件包,我只想跟踪我手动安装的软件包。在理想的情况下,Apt应该改变这种传递性安装的标记方式:git丢失了自动安装的父级,但与另一个软件包有关联,应将其标记为手动安装。 - Alexander Traud
1@AlexanderTraud 你对手动安装的问题和个人定义似乎与这里给出的答案不同。MestreLion和我都向你指出了处理建议包的多种替代方法。如果你需要不同的答案,你应该看看其他答案或者提一个新问题。 - jmiserez

在较新的apt软件包版本中,还有apt-mark命令。
apt-mark showmanual

确实是我正在寻找的答案。 - Wolfer
62这显示了比我手动安装的包要多得多。 - Umang
2@Umang 你说得对。我会说在我写这个答案的时候不是这样的。在我的系统上,没有理由将linux-image-3.11.0-*-generic等视为手动操作。 - Daniel Alder
4@Umang也许这个链接可以帮到你:http://askubuntu.com/questions/432743/how-are-packages-classified-in-apt-mark-showauto-showmanual,但是回答并没有被接受。事实上,一个新安装的很多软件包已经被标记为手动。但仍然有一些奇怪的情况。以我的例子来说:`linux-image-3.13.0-24-generic`是手动的,但当前的`linux-image-3.13.0-27-generic`是自动的。似乎在升级相关包(在这种情况下是`linux-image-generic`)时,手动标记会自动设置。 - Daniel Alder
11@DanielAlder 一些新安装的软件包应该被标记为手动安装。如果没有任何软件包被标记为手动安装,使用 apt-get autoremove 命令可能会删除整个系统。这绝对不是你想要的结果。 - Anton K
1@AntonK 通常使用ubuntu-minimalubuntu-standard软件包来完成这个操作,不需要将它们的任何依赖标记为手动安装。 - OrangeDog
哇,都已经2021年了,我竟然还得找到这篇帖子才能了解apt-mark - Hinz

要获取所有软件包的列表(未安装、由用户安装或默认安装,跨所有PPA),apt使用以下方法:
apt list [选项]
对于此操作,可能有用的选项有:
--installed 仅显示已安装在系统上的软件包(超过50,000个)
--manual-installed 列出通过命令明确安装的软件包,无论是直接安装还是作为依赖项。
或者,您可以执行以下操作:
apt list --installed | grep -F \[installed\] 以获取仅由用户命令及其依赖项生成的软件包列表,并获取有关它们的其他信息,例如支持的版本和体系结构(x86、x86_64、amd64、all等)。

5应该是被接受的答案,适用于现代的Ubuntu系统。 - Déjà vu
1这仍然显示了基本安装中包含的软件包列表,这些软件包并不是由运行apt install ...命令的用户明确安装的。 - Elliott Slaughter

对于Ubuntu 16.04,请查看日志文件/var/log/apt/history.log
例如:
zgrep 'Commandline: apt' /var/log/apt/history.log /var/log/apt/history.log.*.gz

这不是完美的,但它在清楚地显示我手动安装了什么方面做得相当不错。在grep命令中加上-B 1可以看到它是何时安装的。
示例输出
Commandline: apt install postgresql-9.5-plv8
Commandline: aptdaemon role='role-install-file' sender=':1.85'
Commandline: apt install task
Commandline: apt autoremove
Commandline: apt install atom
Commandline: apt upgrade
Commandline: apt-get install asciinema
Commandline: apt install iperf3
Commandline: apt upgrade
Commandline: apt-get install chromium-browser
Commandline: apt install joe cpanminus build-essential postgresql libdbd-pg-perl libcrypt-openssl-bignum-perl libcrypt-openssl-rsa-perl libio-socket-ssl-perl libnet-ssleay-perl libssl-dev
Commandline: aptdaemon role='role-commit-packages' sender=':1.2314'
Commandline: apt install git
Commandline: apt install sqlite
Commandline: apt install whois
Commandline: apt install libdbd-pg-perl
Commandline: apt install perl-doc
Commandline: apt upgrade

不确定这是否能够捕捉到aptitude。它似乎无法从Ubuntu软件桌面应用程序中获取安装信息。

2你说它不完美,但它确实很美。 - activedecay
这应该更新为被接受的答案。 - bomben
你可以解析这个命令来获取所有安装但不包括删除/清除的命令行:comm -23 <(zgrep "Commandline: \(apt\|apt-get\) install" /var/log/apt/history.log* | sed -n 's/^Commandline: \(apt\|apt-get\) install //p' | tr " " "\n" | sort -u) <(zgrep "Commandline: \(apt\|apt-get\) \(remove\|purge\)" /var/log/apt/history.log* | sed -n 's/^Comma ndline: \(apt\|apt-get\) \(remove\|purge\) //p' | tr " " "\n" | sort -u) - jmiserez

执行

apt-mark showauto | grep -iE '^foobar$' 命令,如果该软件包是自动安装的,则会输出 "foobar",否则不会有任何输出。

执行

aptitude search '!~M ~i' 命令,将列出未自动安装的软件包。很遗憾,在 Ubuntu Desktop 10.10 开始,默认安装中将不再包含 aptitude 软件。


aptitude search 显示的是所有软件包,而不仅仅是手动安装的软件包(我猜这就是原帖作者想要的)。 - Oli
1@Oli:研究一下适用于搜索模式的能力;我在那里使用的模式应该正好符合原帖作者的要求。 - Li Lo
我运行了它。它显示了一大堆未安装的软件包。 - Oli
8这个有些不对劲,我正在使用 aptitude search '!~M ~i' 命令,它列出了1043个软件包。但我绝对没有手动安装那么多软件包。 - ThatGraemeGuy
这绝对不能按要求工作,还会打印预装的软件包。 - Irfy
@GraemeDonaldson和Irfy:我觉得Ubuntu(?)有一个bug,在某些情况下,它将系统包列为手动安装的包。我在我的系统上也看到了同样的情况(通过synaptic检查),像"accountservice"这样的包被列为手动安装,而我确定我没有手动安装它。 - Li Lo
在我的系统上,aptitude search '!~M ~i'显示了确切的1583行,比我预期的要多得多。但是apt-mark showmanual也是如此,所以至少它们是一致的。 - Peter V. Mørch
拒绝接受这个答案,因为正如大家所指出的,它似乎并不实际有效。然而,其他的答案似乎也不起作用。我现在并不需要解决方案(这是一个非常旧的问题),但为了保持准确性,我会接受任何真正有效的答案。 - Umang

我想提供一个图形用户界面的解决方案。

enter image description here

  1. 打开Synaptic软件包管理器。

  2. 进入“状态”选项。

  3. 点击“已安装(手动)”。

这将给出通过apt或aptitude手动安装的软件包列表。

不幸的是,在“自定义过滤器”中找不到任何选项来确定是否手动安装了foobar软件包。

如果软件包在“已安装”下但不在“已安装(手动)”下,则表示它是自动安装的。如果软件包在“已安装(手动)”下,则表示它是手动安装的。


以下脚本将打印出所有未设置为自动安装的软件包,因此是手动安装的:
#!/usr/bin/python

try:
    import apt_pkg
except ImportError:
    print "Error importing apt_pkg, is python-apt installed?"
    sys.exit(1)

apt_pkg.init()
STATE_FILE = apt_pkg.config.find_dir("Dir::State") + "extended_states"
auto = set()
tagfile = apt_pkg.TagFile(open(STATE_FILE))
while tagfile.step():
    pkgname = tagfile.section.get("Package")
    autoInst = tagfile.section.get("Auto-Installed")
    if not int(autoInst):
        auto.add(pkgname)
print "\n".join(sorted(auto))

它基于apt-mark如何打印出自动安装的软件包。

给你致敬,尊贵的先生。与被接受的答案相比,这个方法确实管用。 - Irfy
给我展示几个套餐吧,肯定还有很多其他的。 - Rmano
我也是,安装完手动安装的软件包后,肯定会有所遗漏。 - David Ljung Madison Stellar
在较新版本的Python中,如果没有导入sys模块而使用sys.exit(1)可能会导致错误。要么导入sys模块,要么使用exit(1) - Videonauth

正如一些人所评论的,apt-mark showmanual似乎有点bug(我已经报告了错误 727799)。当我使用它时,实际上它报告了很多根本没有记录在/var/lib/apt/extended_states(这是应该存储在那里的)中的东西,并且apt-get并没有将安装的内容记录在/var/lib/apt/extended_states中(只有在/var/lib/dpkg/status中)。上面txwikinger提供的python脚本直接从/var/lib/apt/extended_states中获取信息,但如果您今天使用它,语法可能无法工作(我的Kubuntu 13.10刚开始生成错误)。更新后的语法如下:

#!/usr/bin/python
import sys

try:
    import apt_pkg
except ImportError:
    print "Error importing apt_pkg, is python-apt installed?"
    sys.exit(1)

apt_pkg.init()
STATE_FILE = apt_pkg.config.find_dir("Dir::State") + "extended_states"
auto = set()
tagfile = apt_pkg.TagFile(open(STATE_FILE))
while tagfile.step():
    pkgname = tagfile.section.get("Package")
    autoInst = tagfile.section.get("Auto-Installed")
    if not int(autoInst):
        auto.add(pkgname)
print "\n".join(sorted(auto))

对我来说,这只是一个非常简短的5项清单,似乎也不准确。

1在较新版本的Python中,如果没有导入sys模块而使用sys.exit(1)可能会导致错误。要么导入sys模块,要么使用exit(1) - Videonauth

如果您在虚拟机(Virtual Machine)中,则以下方法可能更适合您:
egrep 'apt(-get)? +install' /var/log/apt/history.log

这个命令可以找到你通过apt和apt-get安装的软件。如果你想包括aptitude,可以使用以下命令:
egrep 'apt(-get|itude)? +install' /var/log/apt/history.log

(但不要认为这会直接找到通过dpkg安装的软件包——反正我也不怎么用。) 原因:如果虚拟机已经包含了一些由虚拟机维护者安装的默认软件(在基础镜像中),而不是由您安装的软件,那么这个答案将不会显示他们安装的软件(至少在我使用的虚拟机中不会),只会显示您安装的软件。
其他答案使用例如apt-mark showmanualaptitude search '!~M ~i'apt list --manual-installed的方法,在我的情况下,在虚拟机中显示了90%对我来说不感兴趣的软件包,这些软件包并非我安装的,而是一些虚拟机基础镜像的维护者安装的(我猜测)。

这是真的。我使用 history | egrep 'apt(-get|itude)? +install' - VidathD
我觉得你的搜索表达式缺少了 apt -y install - bers

如果没有人用apt-某个命令给你一个好的答案,你可以选择以困难的方式来解决。Apt-get将其信息存储在/var/lib/apt/extended_states中。任何自动安装的文件都将添加到此文件中。如果你手动安装了已经在该文件中的软件包,该软件包将保留在该文件中,但在第二行中会显示Auto-installed: 0。它不会被删除。

注意:预计会出现更好的答案,如果文件位置发生变化可能会有效。我会保留我的答案,以防文件位置信息有用。


1不。我快速查看了那个文件,发现liferea被标记为自动安装。我执行了apt-get install liferea,但它没有安装,而是输出了一些类似于“标记为手动安装”的内容。现在liferea仍然在文件中,只是下一行的数字从1变成了0。另外,你应该将正则表达式模式改为" foobar$",而不仅仅是foobar - Umang
没错。是我的错,在我的系统中没有0这一行,但这应该是个罕见的情况。我更新了答案以防万一。 - Javier Rivera