apt-get remove with 通配符删除方式超出预期。为什么?

Ste*_*ich 50 package-management apt regex

昨晚我试图刻录CD。对 k3b 感到恼火并选择使用 brasero,我去删除了 k3b。

我输入:

sudo apt-get remove k3b
Run Code Online (Sandbox Code Playgroud)

我点击了两次 Tab,看到我的系统上有 k3b 和 k3b-data。假设在没有 k3b 的情况下我的系统上不需要 k3b-data,我也想删除它,所以我输入:

sudo apt-get remove k3b*
Run Code Online (Sandbox Code Playgroud)

不幸的是,我没有看就按 Y 确认。它卸载了很多k3bk3b-data。它卸载了不适合我的k3b*正则表达式的软件包。例如:transmissionnetwork-manager

我相当确定我k3b和和之间没有空格,*但我不知道为什么它会删除它所做的所有事情。我对 apt-get 有什么误解吗?

Bor*_*ein 62

你想要的命令是sudo apt-get remove '^k3b.*',因为:

  • 您需要.*匹配任何字符,任意次数
  • 您需要^匹配字符串的开头
  • 您需要引用正则表达式以防止 bash 解释*为通配符

(此答案完善并总结了 qbi 和 Flimm 提供的先前信息)

  • 这是安全的,可以使用它,但您不需要`.*`。你可以只使用`sudo apt-get remove ^k3b`。`^` 的存在足以使参数被解释为正则表达式,并且当 `apt` 或 `apt-get` 将参数解释为正则表达式时,它会在包名称中的任何地方*匹配它。这就是为什么你需要 `^`--to [anchor](http://www.regular-expressions.info/anchors.html) 匹配包名称的开头。正则表达式不需要匹配整个包名,只需匹配它的任何部分。 (6认同)
  • 请注意,这适用于 apt-get 而不是 apt。 (2认同)

qbi*_*qbi 37

正则表达式*代表零或任意多个。所以你告诉apt-get删除任何包含 k3后跟任意数量的b,所以基本上所有包含k3. 如果我在我的系统上尝试你的命令,它想删除 58 个包。

sudo apt-get remove -s k3b*
Package k3b is not installed, so not removed
Package k3b-data is not installed, so not removed
Package k3b-dbg is not installed, so not removed
Package libcanberra-gtk3-0 is not installed, so not removed
Package libcanberra-gtk3-0-dbg is not installed, so not removed
Package libcanberra-gtk3-dev is not installed, so not removed
…
The following packages will be REMOVED:
  appmenu-gtk ardour audacity brasero brasero-cdrkit firefox-globalmenu
  gconf-editor gir1.2-appindicator-0.1 gnome-applets gnome-control-center
…
0 upgraded, 2 newly installed, 58 to remove and 0 not upgraded.
Run Code Online (Sandbox Code Playgroud)

  • `*` 确实像在 DOS 中一样用作 bash 的通配符,但是像 `apt-get` 这样的一些命令需要一个正则表达式。当您键入 `sudo apt-get remove -s k3b*` 时,bash 将首先在您当前目录中查找以 `k3b` 开头的所有文件。如果找到,它将用这些文件名替换该参数。如果没有,它会将 `k3b*` 直接传递给 `apt-get`,后者会将其解释为正则表达式。如果您不希望 bash 首先将星号解释为通配符(您可能不这样做),请用单引号将参数括起来,如下所示:`sudo apt-get remove -s 'k3b*'` (14认同)
  • 所以预期的命令应该是`sudo apt-get remove -s 'k3b.*'`。只是偶然发现了这个答案,发现知道它真的很重要。恕我直言,这是非常出乎意料的,我会将其标记为 apt-get 的“意外行为”错误……如果未指定,您通常期望“glob”含义而不是“regexp”含义。无论如何,谢谢+1! (3认同)

Eli*_*gan 12

使用sudo apt-get remove ^k3b来代替。当您安装或删除软件包时,*通常很危险并且很少需要。如果您确实使用了*,则应该引用它,但这并不会使它更安全,因为它选择比预期多得多的包的趋势是方式aptapt-get解释它的结果,而不是路径名扩展的影响。

  • 即使是安全的使用*常常是不必要的
  • 不安全的使用是残酷的。删除k3b*会删除名称中包含k3 任何位置的每个包(以及依赖于此类包的每个包)。这不是打字错误——k3即使没有b,包含也足够了,因为b*意味着“零个或多个bs”。

当运行aptapt-getinstallremovepurge动作,每个后续参数是第一1解释为个别包的名称。如果存在具有该确切名称的包,则会为其执行操作。

如果不存在这样的包,apt并且apt-get将检查参数包含任何常见的正则表达式 元字符2 .?+*|\[^,或$。如果没有,就完成了——没有找到包。

如果它确实包含任何这些字符,则将其视为正则表达式并与任何包名称的任何部分进行匹配。它不必与全名匹配。正如其他人所说,*在正则表达式中与*在 glob 中的含义不同。?也没有。在正则表达式中:

  • *允许前一项出现任意次数——包括只出现一次或根本不出现——而不是恰好一次。
  • ?使前一项成为可选——也就是说,它允许它出现零次或一次。

apt-get(8) ( man apt-get) 说:

如果没有包与给定的表达式匹配并且表达式包含“.”、“?”之一 或 '*' 那么它被假定为一个 POSIX 正则表达式,它被应用于数据库中的所有包名称。然后安装(或删除)任何匹配项。请注意,匹配是由子字符串完成的,因此 'lo.*' 匹配 'how-lo' 和 'lowest'。如果这是不希望的,请使用 '^' 或 '$' 字符锚定正则表达式,或者创建更具体的正则表达式。

联机帮助页只提到了., ?, 和*,但并不完整, as +, |, [, ^,$也足以让apt-getapt解释模式为正则表达式。3

虽然可以匹配任何数量的任何字符.*-只是*-您只需要这个,如果它会出现在中间的正则表达式。因为模式与包名称的任何子字符串匹配,所以在模式的末尾(或开头)是没有意义的。

联机帮助页提到了^$。这些(尤其是^)的关键是编写安全,高效的方式与使用installremove或者purge在行动aptapt-get

  • ^将正则表达式锚定到整个字符串的开头^k3b选择其名称的所有包开始使用k3b
  • $将正则表达式锚定到整个字符串的末尾k3b$将选择其名称的所有包结尾k3b

因此,您可以使用此命令安全地删除软件包:

sudo apt-get remove ^k3b
Run Code Online (Sandbox Code Playgroud)

最后,在您提到的特定情况下,您不妨自己传递两个名称:

sudo apt-get remove k3b k3b-data
Run Code Online (Sandbox Code Playgroud)

然后你就避免了所有这些复杂性!(虽然^一旦你习惯了锚定很简单。)或者使用大括号扩展,你的外壳将其扩展为上述命令:

sudo apt-get remove k3b{,-data}
Run Code Online (Sandbox Code Playgroud)

1 有两个例外:(a)中的一些选项(例如,-f--purge)被识别,和 (b)中出现在某些标点字符结束,否则将被认为是包的名称以执行动作可以是一个参数的用于改变所做的事情(例如,sudo apt install ubuntu-desktop^安装任务而不是包,以及何时^出现在最后)。

2 存在其他正则表达式元字符。例如,\正则表达式的所有方言都支持并且常用。., ?, +, *, |, [, ^, 和$恰好是 APT 开发人员决定将触发解释为正则表达式的元字符(在解析为一个确切的包命名失败后)。

3 验证这一点的最简单方法是使用上述-s选项模拟具有此类模式的安装或移除。例如,运行apt -s install ^virtualbox显示sudo apt install ^virtualbox将尝试安装包管理器知道名称以virtualbox. 但是,这种行为也可以通过检查源代码来验证。检查中的CacheSetHelper::PackageFromRegEx功能cacheset.cc