如果另一个APT实例正在运行,如何使程序包管理器等待?


50

我已经看到了许多软件,例如Update Manager和Synaptic Package Manager,它们在其他程序正在使用/var/lib/dpkg/lock和时被锁定。我们如何通过终端做到这一点?我看到了apt-get的手册,但没有发现任何有用的信息。


好了,您可以执行几个apt-get命令。就像sudo apt-get install packagename && sudo apt-get update,它们会彼此自动发生。
Alvar

1
这不是这样做的sudo apt-get install packagename1 packagename2 packagename3 吗?
Rinzwind

(见unix.stackexchange.com/questions/463498/...如果你是在你试图等待开机的时候容易完成在Ubuntu 18.04及以上的特殊情况)
匿名

Answers:


35

您可以使用aptdcon命令联机帮助页图标通过与aptdaemon通信而不是直接使用apt-get来使程序包管理器任务排队。

因此,基本上您可以执行sudo aptdcon --install chromium-browser任何操作,并且在该命令运行时可以再次运行它,但是安装不同的软件包,而apt-daemon只会将它们排队,而不是出错。

如果您要进行长期升级或执行某些操作,并且希望继续安装软件包,或者要一起编写脚本,并希望确保安装内容将更加可靠,则此功能特别有用。


4
可以aptdcon运行以使其接受任何提示apt-get -y吗?
Noki 2015年

4
yes | aptdcon --hide-terminal --install "package"需要Hide Terminal,否则管道yes会引起问题。
whitehat101

1
这个答案的问题是它需要安装aptdcon。我正在做一个引导脚本,该脚本执行VPS的初始设置,其中后台进程也在执行一些初始设置。aptdcon在解决锁定问题之前,我无法安装。
假名称

2
@FakeName用于初始服务器配置,您可能要使用cloud-init:cloudinit.readthedocs.io/en/latest
Jorge Castro

1
里面有什么包裹aptdcon
ctrl-alt-delor

45

apt-get如果正在运行其他软件管理器,则可以学习等待。与下一个屏幕投射的行为类似:

在此处输入图片说明

我是怎么做到的?

我在目录中使用以下bash代码创建了一个名为apt-get(为的包装apt-get)的新脚本/usr/local/sbin

#!/bin/bash

i=0
tput sc
while fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
    case $(($i % 4)) in
        0 ) j="-" ;;
        1 ) j="\\" ;;
        2 ) j="|" ;;
        3 ) j="/" ;;
    esac
    tput rc
    echo -en "\r[$j] Waiting for other software managers to finish..." 
    sleep 0.5
    ((i=i+1))
done 

/usr/bin/apt-get "$@"

不要忘记使其可执行:

sudo chmod +x /usr/local/sbin/apt-get

在测试之前,请检查一切是否正常。which apt-get命令的输出应为now /usr/local/sbin/apt-get。原因是:默认情况下,目录在user或root中位于/usr/local/sbin目录之前。/usr/binPATH


多数民众赞成真的直接打到我的问题。谢谢:)
rɑːdʒɑ

2
您的脚本如何管理apt-get的堆叠实例?像,在完成之前,我还要启动5个apt-get吗?
Braiam

@Braiam真诚的,我不知道;我不是一个好的测试员。你测试了吗?如果出现问题,可以通过在脚本的新实例启动时创建带有时间戳的新锁来改进脚本(仅作为示例-还有许多其他方法)。
RaduRădeanu13年

哇!雄辩。到目前为止,我所投入的一切都像魅力一样起作用!两个大拇指...这应该只是软件包的一部分:D
Eric Eric

2
那很完美!我在使用AWS cloudinit在新服务器上安装软件包时遇到了问题,但是Ubuntu apt在启动时会做一些事情,因此dpkg由于dpkg db锁定,我的命令失败了。我已经在Cloudinit用户数据中添加了这种单行代码:while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done; dpkg -i package.deb它非常有用。
Volodymyr Smotesko'17年

20

除了显而易见的以外&&,您可能正在寻找aptdcon。此工具能够检测apt的其他实例并等待它们完成:

sudo aptdcon-安全升级
[/] 11%等待其他软件经理退出等待能力

(我在其他地方有能力)

该工具的优点是您可以连续存储多个操作,而不必担心接下来要做什么。aptdcon非常适合无人参与脚本和GUI安装,因为您可以允许该工具在后台运行,以免阻塞您的前端。

支持的操作aptdcon有:

  • --refresh-c:相当于apt-get update。它会更新您的包裹清单。
  • --install--remove--upgrade--purge--downgrade。他们每个人都照他们的名字说的做。包装名称是必填项。-i-r-u-p:这些都是所有除降级,谁不有一个短的选项。
  • --safe-upgrade--full-upgrade是同行apt-getupgrade/ dist-upgradeaptitudesafe-upgrade/ full-upgrade。这些不需要参数。
  • 手册中还有其他几种操作。但是,这些是感兴趣的用户最常使用的aptd。有迹象表明,什么重叠选项apt-keyapt-cachedpkg做的。

apt-get本身不支持此类方法(以等待apt的其他实例),因此aptdconGUI的程序包管理器的首选解决方案也是如此:USC aptd用作后端,与Synaptic相同。其他解决方案是packagekit,但它不支持您正在寻找的功能(尚未)。


1
aptdcon --install /path/to/pgk.deb像一样工作dpkg -i,尽管我找不到手册中明确提到的内容。
whitehat101

我可以在软件包的安装后脚本中使用它吗?
尼尔森·特谢拉

@NelsonTeixeira为什么要在ppst-install脚本中使用它?如果正在执行安装后,显然apt正在运行。
Braiam '17年

我想让安装后安装一些不在存储库中的特殊软件包。这可能吗 ?我将它们包含在软件包中,然后脚本将在安装完成后安装它们。
尼尔森·特谢拉

再次@NelsonTeixeira,为什么有必要?安装后脚本仅在安装软件包时运行。我建议您改问一个问题,并详细解释您的情况。
Braiam

18

一种非常简单的方法是等待锁未打开的脚本。让我们对其进行调用waitforapt并坚持下去/usr/local/bin

#!/bin/sh

while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do
   sleep 1
done

然后运行sudo waitforapt && sudo apt-get install whatever。您可以在其中添加例外,sudoers以使您无需密码即可运行它(因为需要它,apt-get因此不会有太大的收获)。

不幸的是,这不会排队。鉴于apt的某些操作是交互式的(“您确定要删除所有这些软件包吗?!”),所以我看不出解决此问题的好方法...


也许-y对所有操作都假设是?
Braiam

谢谢。我想使用别名帮助可以更好地使用它。
rɑːdʒɑ

3
-y虽然不确定是否可取。
奥利(Oli)

1
我不认为你想包装sudo fuser[[ $(...) ]]。当文件被访问时,它返回零退出代码,这样就足够了。
kiri

4
我发现以下解决方案更加完善,因为使用apt-get时涉及多个锁定: while sudo fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do echo 'Waiting for release of dpkg/apt locks'; sleep 5; done; sudo apt-get -yy update
ManuelKießling17年

3

您可以使用一种轮询技术:

$ time (while ps -opid= -C apt-get > /dev/null; do sleep 1; done); \
  apt-get -y install some-other-package

更好地使用inotify
ctrl-alt-delor

3

我做了一个脚本来做到这一点:

#!/bin/bash

# File path to watch
LOCK_FILE='/var/lib/dpkg/lock'

# tput escape codes
cr="$(tput cr)"
clr_end="$(tput el)"
up_line="$(tput cuu 1)"

CLEAN(){
    # Cleans the last two lines of terminal output,
    # returns the cursor to the start of the first line
    # and exits with the specified value if not False

    echo -n "$cr$clr_end"
    echo
    echo -n "$cr$clr_end$up_line"
    if [[ ! "$1" == "False" ]]; then
        exit $1
    fi
}

_get_cmdline(){
    # Takes the LOCKED variable, expected to be output from `lsof`,
    # then gets the PID and command line from `/proc/$pid/cmdline`.
    #
    # It sets `$open_program` to a user friendly string of the above.

    pid="${LOCKED#p}"
    pid=`echo $pid | sed 's/[\n\r ].*//'`
    cmdline=()
    while IFS= read -d '' -r arg; do
        cmdline+=("$arg")
    done < "/proc/${pid}/cmdline"
    open_program="$pid : ${cmdline[@]}"
}

# Default starting value
i=0

# Checks if the file is locked, writing output to $FUSER
while LOCKED="$(lsof -F p "$LOCK_FILE" 2>/dev/null)" ; do
    # This will be true if it isn't the first run
    if [[ "$i" != 0 ]]; then
        case $(($i % 4)) in
            0 ) s='-'
                i=4
                _get_cmdline # Re-checks the command line each 4th iteration
            ;;
            1 ) s=\\ ;;
            2 ) s='|' ;;
            3 ) s='/' ;;
        esac
    else
        # Traps to clean up the printed text and cursor position
        trap "CLEAN False; trap - SIGINT ; kill -SIGINT $$" SIGINT
        trap 'CLEAN $((128+15))' SIGTERM
        trap 'CLEAN $((128+1))' SIGHUP
        trap 'CLEAN $((128+3))' SIGQUIT

        # Default starting character
        s='-'

        _get_cmdline
        echo -n "$save_cur"
    fi
    # Prints the 2nd line first so the cursor is at the end of the 1st line (looks nicer)
    echo
    echo -n "$cr$clr_end$open_program"
    echo -n "$up_line$res_cur$cr$clr_end[$s] Waiting for other package managers to finish..."
    #echo -en "$cr$clr_end[$s] Waiting for other package managers to finish..."
    #echo -en "\n$cr$clr_end$open_program$cr$up_line"
    ((i++))
    sleep 0.025
done

CLEAN False

# This allows saving the script under a different name (e.g. `apt-wait`)
# and running it. It only imitates `apt-get` if it was launched as such
if [[ "${0##*/}" == 'apt-get' ]]; then
    exec /usr/bin/apt-get "$@"
    exit $?
fi

将以上内容保存到中/usr/local/sbin/apt-getapt-get如果另一个实例已经在运行,它将等待。

或者,将其另存为/usr/local/sbin/apt-wait,示例用法:

apt-wait && aptitude

它会aptitude在当前持有锁的进程退出后运行。

示例运行:

  1. 首先,apt-get运行命令,例如:

    $ sudo apt-get remove some_package
    
  2. 然后,在另一个终端中,运行另一个命令:

    $ sudo apt-get install some_other_package
    

    它将等待第一个命令完成然后运行。等待时输出:

    [/] Waiting for other package managers to finish...
          28223 : /usr/bin/apt-get remove some_package
    

1
更好地使用inotify
ctrl-alt-delor

3

不幸的是,当您在不同的非特权名称空间容器(例如lxc)中运行时,fuser并不会为您带来很多帮助。

另外,默认情况下未安装aptdcon(至少在18.04上安装),并且使任务在队列中后台运行,从而导致序列化丢失。这不是无法克服的,但这确实意味着您的自动化需要某种方式来避免在安装aptdcon时apt中出现群发错误,并且您需要某种等待循环,以便通过aptdcon安装软件包后需要序列化的任何东西除非已经有某种标志。

起作用的是羊群。这也应该在NFS等上工作,因为它以与apt相同的方式使用文件系统锁定,仅使用-w seconds参数,它将等待您的锁定而不抛出错误。

因此,遵循包装模型,将其作为apt-get添加到/ usr / local / bin /中并共享。

通过不允许apt上的并行性,这还具有限制IO的好处,因此您可以让cron在任何午夜的任何地方触发更新而不会破坏磁盘。

#!/bin/bash
exec /usr/bin/flock -w 900 -F --verbose /var/cache/apt/archives/lock /usr/bin/apt-get $@  

对apt-get的非常好且简单的功能请求将是-w标志,以切换到阻止/等待锁。


apt使用fcntl而不是flock,所以这将不起作用。askubuntu.com/questions/1077215/...
安迪·

fuser许多答案中所示的用法相比,此方法效果很好,并且是一个更好的选择,因为fuser它仍然易于在观察锁和目标进程再次抓住锁之间发生竞争。有了flock锁后,该锁便被获取,然后传递给目标进程,因此在此期间没有时间丢失锁。
jrudolph

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.