Как заставить менеджер пакетов ждать, если запущен другой экземпляр APT?

Я видел много программ, таких как Update Manager и Synaptic Package Manager, они ждут, если какая-то другая программа использует /var/lib/dpkg/lock и заблокирован. Как мы можем сделать это через Терминал? Я видел apt-getРуководство, но ничего полезного не нашел.

10 ответов

Решение

Вы можете использовать aptdcon команда Значок Manpage ставить в очередь задачи диспетчера пакетов, связываясь с aptdaemon вместо непосредственного использования apt-get.

Так что в основном вы можете просто сделать sudo aptdcon --install chromium-browser или что-то еще, и пока эта команда выполняется, вы можете запустить ее снова, но установить разные пакеты, и apt-daemon просто поставит их в очередь, а не выдает ошибку.

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

Ты можешь сделать apt-get научиться ждать, если другой менеджер программного обеспечения работает. Нечто подобное с поведением из следующего скриншота:

введите описание изображения здесь

Как я это сделал?

Я создаю новый скрипт с именем apt-get (обертка для apt-get) в /usr/local/sbin каталог со следующим кодом bash внутри:

#!/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 команда должна быть сейчас /usr/local/sbin/apt-get, Причина в том, что по умолчанию /usr/local/sbin каталог размещен перед /usr/bin каталог пользователя или root PATH,

Очень простым подходом будет сценарий, который ожидает, пока блокировка не будет открыта. Давайте назовем это 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 являются интерактивными ("Вы уверены, что хотите удалить все эти пакеты?!"), я не могу найти хороший способ обойти это...

Помимо очевидного &&, вы можете искать aptdcon, Этот инструмент может обнаружить другие экземпляры apt и дождаться их завершения:

sudo aptdcon --safe-upgrade
[/] 11% В ожидании выхода других менеджеров программного обеспечения В ожидании способности

(Я работаю в другом месте)

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

Операции, поддерживаемые aptdcon являются:

  • --refresh, -c: Это эквивалентно apt-get update, Он обновляет ваш список пакетов.
  • --install, --remove, --upgrade, --purge, --downgrade, Каждый из них делает, как говорят их имена. Название пакета (ов) является обязательным. -i, -r, -u, -p: это короткие варианты для всех, кроме рейтинга, у которого его нет.
  • --safe-upgrade, --full-upgrade являются аналогами apt-get"s upgrade/dist-upgrade а также aptitude"s safe-upgrade/full-upgrade, Это не нуждается в параметрах.
  • Есть несколько других операций, которые можно найти в руководстве. Но это наиболее используемые пользователи, заинтересованные в aptd, Есть варианты, которые совпадают с тем, что apt-key, apt-cache, dpkg делать.

apt-get сам не поддерживает такие методы (ждать других экземпляров apt), поэтому aptdcon является предпочтительным решением для менеджеров пакетов GUI: USC использует aptd как back-end, такой же как Synaptic. Другое решение packagekit, но он не поддерживает функцию, которую вы ищете (пока).

С 1.9.11 aptа также apt-getесть опция, позволяющая дождаться освобождения блокировок dpkg.

Использовать DPkg::Lock::Timeoutвозможность установить время ожидания в секундах для команды apt-get. Этот пример будет ждать 60 секунд:

      sudo apt-get -o DPkg::Lock::Timeout=60 install packagename

Если вы установите это значение равным -1, оно будет ждать вечно.

      sudo apt-get -o DPkg::Lock::Timeout=-1 install packagename

Для получения дополнительной информации см.: Ожидание блокировок apt без хакерских скриптов bash . Эта опция была добавлена ​​в apt-get в феврале 2020 года .

Однострочник на основе ответа Оли:

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

Вы можете использовать технику опроса:

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

К сожалению, fuser мало что делает для вас, когда вы работаете в разных непривилегированных контейнерах пространства имен, таких как lxc.

Кроме того, aptdcon не устанавливается по умолчанию (по крайней мере, 18.04) и помещает вашу задачу в очередь, поэтому вы теряете сериализацию. Это не является непреодолимым, но это означает, что ваша автоматизация должна иметь какой-то способ избежать ошибок в apt при установке aptdcon, и вам понадобятся циклы ожидания для всего, что вам нужно сериализовать после установки пакетов через aptdcon если для этого уже нет какого-то флага.

Что работает, так это стадо. Это также должно работать через NFS и т. Д., Поскольку он использует блокировку файловой системы точно так же, как и apt, только с параметром -w секунд он будет ожидать блокировки, а не выдавать ошибку.

Поэтому, следуя модели оболочки, добавьте это как apt-get в /usr/local/bin/ и поделитесь.

Это также имеет преимущество в ограничении ввода-вывода, так как не допускает параллелизма в apt, так что вы можете позволить cron запускать обновления в полночь везде, не перегружая диск.

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

Очень хорошим и простым запросом функции для apt-get будет флаг -w для переключения на блокировку / ожидание блокировки.

Я сделал скрипт, который делает это:

#!/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-get, apt-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
    

В зависимости от того, переносите ли вы все обращения к apt-get или нет, это может сработать:

flock ./apt.lock apt-get install ...

Создайте свой собственный замок с flock(1) вместо того, чтобы пытаться перехватить различные dpkg замки.

Другие вопросы по тегам