Come far attendere un gestore pacchetti se è in esecuzione un'altra istanza di APT?


50

Ho visto molti software come Update Manager e Synaptic Package Manager, che aspettano se qualche altro programma sta usando /var/lib/dpkg/locke è bloccato. Come possiamo farlo attraverso il Terminale? Ho visto apt-getil manuale ma non ho trovato nulla di utile.


bene puoi fare diversi comandi apt-get. Come sudo apt-get install packagename && sudo apt-get updatee accadranno automaticamente dopo l'altro.
Alvar,

1
Questo non fa questo sudo apt-get install packagename1 packagename2 packagename3 :?
Rinzwind

(Vedi unix.stackexchange.com/questions/463498/… se sei nel caso speciale in cui stai cercando di aspettare il tempo di avvio per finire su Ubuntu dal 18.04 in poi)
Anon,

Risposte:


35

È possibile utilizzare il aptdconcomandoIcona Manpage per mettere in coda le attività del gestore pacchetti comunicando con aptdaemon invece di utilizzare direttamente apt-get.

Quindi in pratica puoi semplicemente fare sudo aptdcon --install chromium-browserqualsiasi cosa e mentre quel comando è in esecuzione puoi eseguirlo di nuovo ma installare pacchetti diversi e apt-daemon li metterà in coda invece di sbagliare.

Ciò è particolarmente utile se stai facendo un lungo aggiornamento o qualcosa e vuoi continuare a installare i pacchetti o se stai scrivendo qualcosa insieme e vuoi assicurarti che l'installazione sia più affidabile.


4
Può aptdconessere eseguito in modo tale da accettare qualsiasi prompt come apt-get -yfa?
Noki,

4
yes | aptdcon --hide-terminal --install "package"È necessario Nascondi terminale, altrimenti le tubazioni yescauseranno problemi.
whitehat101,

1
Il problema con questa risposta è che richiede l'installazione aptdcon. Sto lavorando a uno script bootstrap che esegue la configurazione iniziale di un VPS, in cui anche un processo in background sta eseguendo una configurazione iniziale. Non riesco a installare aptdconfinché non riesco a aggirare il blocco.
Nome falso

2
@FakeName per la configurazione iniziale del server che probabilmente vorresti usare cloud-init: cloudinit.readthedocs.io/en/latest
Jorge Castro,

1
in quale pacchetto aptdcon?
ctrl-alt-delor,

45

Puoi farti apt-getimparare ad aspettare se è in esecuzione un altro software manager. Qualcosa di simile al comportamento del prossimo cast dello schermo:

inserisci qui la descrizione dell'immagine

Come l'ho fatto?

Creo un nuovo script chiamato apt-get(wrapper per apt-get) nella /usr/local/sbindirectory con all'interno il seguente codice 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 "$@"

Non dimenticare di renderlo eseguibile:

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

Prima di provare, controlla che tutto sia a posto. L'output del which apt-getcomando dovrebbe essere ora /usr/local/sbin/apt-get. Il motivo è: per impostazione predefinita, la /usr/local/sbindirectory viene posizionata prima della /usr/bindirectory in utente o root PATH.


questo è davvero un colpo diretto alla mia domanda. grazie :)
rɑːdʒɑ

2
In che modo il tuo script gestisce istanze sovrapposte di apt-get? Tipo, prima che finisca lancio 5 altri apt-get?
Braiam,

@Braiam Cordiali saluti, non lo so; Non sono un buon tester. L'hai provato? Se compaiono problemi, lo script può essere migliorato creando un nuovo blocco con timestamp all'avvio di una nuova istanza dello script (solo un esempio - ci sono anche molti altri modi).
Radu Rădeanu,

Wow! eloquente. funziona come un incantesimo con tutto ciò che ho lanciato finora! Due pollici in su ... questo dovrebbe far parte del pacchetto: D
Eric

2
È perfetto! Ho avuto problemi con l'uso di AWS cloudinit per installare i pacchetti su un nuovo server, ma Ubuntu fa alcune aptcose all'avvio, quindi il mio dpkgcomando non è riuscito a causa di dpkg db bloccato. Ho inserito questo one-liner nel mio userdata cloudinit: while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done; dpkg -i package.debe funziona benissimo.
Volodymyr Smotesko,

20

A parte l'ovvio &&, potresti essere alla ricerca aptdcon. Questo strumento è in grado di rilevare altre istanze di apt e attendere che finiscano:

sudo aptdcon --safe-upgrade
[/] 11% In attesa dell'uscita da altri gestori di software In attesa di attitudine

(Sto eseguendo aptitude da qualche altra parte)

Il vantaggio di questo strumento è che puoi immagazzinare più azioni consecutivamente senza preoccuparti di quello che farai dopo. aptdconè ideale per gli script non presidiati e l'installazione della GUI, poiché è possibile consentire l'esecuzione dello strumento in background per non bloccare il frontend.

Le operazioni supportate da aptdconsono:

  • --refresh, -c: Questo è l'equivalente di apt-get update. Aggiorna l'elenco dei pacchetti.
  • --install, --remove, --upgrade, --purge, --downgrade. Ognuno di loro fa come dicono i loro nomi. Il nome del / i pacchetto / i è obbligatorio. -i, -r, -u, -p: Queste sono le brevi opzioni per tutti tranne downgrade, che non ha uno.
  • --safe-upgrade, --full-upgradesono le controparti di apt-get"s upgrade/ dist-upgradee aptitude" s safe-upgrade/ full-upgrade. Questi non hanno bisogno di parametri.
  • Ci sono molte altre operazioni, che possono essere trovate nel manuale. Ma questi sono i più utilizzati dagli utenti interessati aptd. Ci sono opzioni che si sovrappongono con quello che apt-key, apt-cache, dpkgfare.

apt-getdi per sé non supporta tali metodi (per attendere altre istanze di apt), quindi aptdconè la soluzione preferita per i gestori di pacchetti della GUI: USC usa aptdcome back-end, come Synaptic. Un'altra soluzione è packagekit, ma non supporta la funzione che stai cercando (ancora).


1
aptdcon --install /path/to/pgk.debFunziona come dpkg -i, anche se non sono riuscito a trovarlo esplicitamente menzionato nel manuale.
whitehat101

Posso usarlo nello script post-installazione in un pacchetto?
Nelson Teixeira,

@NelsonTeixeira perché dovresti usarlo nello script ppst-install? Se viene eseguita la post-installazione, ovviamente apt è in esecuzione.
Braiam,

Voglio fare in modo che postinstall installi alcuni pacchetti speciali che non sono nel repository. È possibile ? Vorrei includerli nel pacchetto e quindi lo script li installerebbe al termine dell'installazione.
Nelson Teixeira,

@NelsonTeixeira di nuovo, perché sarebbe necessario? Gli script post-installazione vengono eseguiti solo in caso di installazione di un pacchetto. Ti suggerisco invece di porre una domanda e di spiegare il tuo caso con maggiori dettagli.
Braiam

18

Un approccio molto semplice sarebbe uno script che aspettava che il blocco non fosse aperto. Chiamiamolo waitforapte inseriamolo /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

Quindi corri e basta sudo waitforapt && sudo apt-get install whatever. Potresti aggiungere eccezioni in sudoersper consentirti di eseguirlo senza bisogno di una password (ne avrai bisogno per il apt-getfatto che non è un grande guadagno).

Purtroppo questo non mette in coda le cose. Dato che alcune delle operazioni di apt sono interattive ("Sei sicuro di voler rimuovere tutti quei pacchetti ?!"), non riesco a vedere un buon modo per aggirare questo ...


forse -ysupporre di sì per tutte le operazioni?
Braiam,

Grazie. Penso di poterlo utilizzare meglio se utilizzo la guida alias.
rʒɑdʒɑ

3
In certo non -yè desiderabile però.
Oli

1
Non credo che si vuole avvolgere sudo fuserin [[ $(...) ]]. Restituisce un codice di uscita zero quando si accede al file, quindi dovrebbe essere sufficiente.
Kiri,

4
Ho trovato la seguente soluzione più completa, dato che ci sono diversi blocchi coinvolti quando si lavora con 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
Manuel Kießling,

3

È possibile utilizzare una tecnica di polling:

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

meglio usareinotify
ctrl-alt-delor

3

Ho realizzato una sceneggiatura che fa questo:

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

Salva quanto sopra in /usr/local/sbin/apt-get. apt-getattenderà quindi se è già in esecuzione un'altra istanza.

In alternativa, salvalo come /usr/local/sbin/apt-wait, esempio di utilizzo:

apt-wait && aptitude

che verrà eseguito aptitudedopo la chiusura del processo corrente che tiene il blocco.

Esempio di esecuzione:

  1. Innanzitutto, apt-getviene eseguito un comando, ad esempio:

    $ sudo apt-get remove some_package
    
  2. Quindi, in un altro terminale, viene eseguito un altro comando:

    $ sudo apt-get install some_other_package
    

    Attenderà il completamento del primo comando, quindi verrà eseguito. Uscita in attesa:

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

1
meglio usareinotify
ctrl-alt-delor

3

Sfortunatamente il fusore non fa molto per te quando corri in diversi contenitori di spazi dei nomi non privilegiati come lxc.

Inoltre, aptdcon non è installato di default (almeno su 18.04) e mette in background l'attività in una coda in modo da perdere la serializzazione. Questo non è insormontabile, ma significa che la tua automazione deve avere un modo per evitare errori di floccaggio in apt durante l'installazione di aptdcon e dovrai avere una sorta di loop di attesa per tutto ciò che devi serializzare dopo aver installato i pacchetti tramite aptdcon a meno che non ci sia già una sorta di bandiera per quello.

Ciò che funziona è il gregge. Questo dovrebbe funzionare anche su NFS ecc. Dato che usa il blocco del file system nello stesso modo di apt, solo con il parametro -w seconds attenderà sul tuo blocco invece di lanciare un errore.

Quindi, seguendo il modello wrapper, aggiungilo come apt-get in / usr / local / bin / e condividi via.

Ciò ha anche il vantaggio di limitare l'IO non consentendo il parallelismo su apt, in modo da poter consentire agli aggiornamenti di trigger cron a mezzanotte ovunque senza battere il disco.

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

Una richiesta di funzionalità molto bella e semplice per apt-get sarebbe un flag -w per passare a un blocco blocco / attesa.


apt usa fcntl, non flock, quindi non funzionerà. askubuntu.com/questions/1077215/…
Andy,

Funziona perfettamente ed è un'opzione migliore rispetto all'uso fusercome mostrato in molte risposte perché fuserè ancora soggetto a condizioni di gara tra la visione del lucchetto e il processo target che afferra di nuovo il lucchetto. Con flockil blocco viene acquisito e quindi passato al processo di destinazione in modo da non perdere tempo nel frattempo.
jrudolph,

1

One-liner basato sulla risposta di Oli :

while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.