I have seen many softwares such as Update Manager and Synaptic Package Manager, they wait if some other program is using the /var/lib/dpkg/lock and is locked. How can we do this through the Terminal? I saw apt-get's manual but didn't find anything useful.
9 Answers
You can make apt-get to learn to wait if another software manager is running. Something similar with the behaviour from the next screen cast:

How I made it?
I create a new script called apt-get (wrapper for apt-get) in /usr/local/sbin directory with the following bash code inside:
#!/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 "$@"
Don't forget to make it executable:
sudo chmod +x /usr/local/sbin/apt-get
Before to test, check if everything is ok. The output of which apt-get command should be now /usr/local/sbin/apt-get. The reason is: by default, the /usr/local/sbin directory is placed before /usr/bin directory in user or root PATH.
- 174,089
- 51
- 332
- 407
You can use the aptdcon command
to queue up package manager tasks by communicating with aptdaemon instead of using apt-get directly.
So basically you can just do sudo aptdcon --install chromium-browser or whatever and while that command is running you can run it again but install different packages and apt-daemon will just queue them up instead of erroring out.
This is especially useful if you're doing a long upgrade or something and want to keep installing packages or if you're scripting something together and want to make sure installing things will be more reliable.
- 28,986
- 73,717
A very simple approach would be a script that waited for the lock to not be open. Let's call it waitforapt and stick it in /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
Then just run sudo waitforapt && sudo apt-get install whatever. You could add exceptions into sudoers to allow you to run it without needing a password (you'll need it for the apt-get so it's no great gain).
Unfortunately this doesn't queue things. Given that some of apt's operations are interactive ("Are you sure you want to remove all those packages?!"), I can't see a good way around this...
- 299,380
Apart of the obvious &&, you may be looking for aptdcon. This tool is able to detect other instances of apt and wait them to finish:
sudo aptdcon --safe-upgrade [/] 11% Waiting for other software managers to quit Waiting for aptitude
(I'm running aptitude somewhere else)
The advantage of this tool is that you can stock several actions consecutively without being worried of what you will be doing next. aptdcon is ideal for unattended scripts, and GUI installation, since you can allow the tool run in background as not to block your frontend.
The operations supported by aptdcon are:
--refresh,-c: This is the equivalent toapt-get update. It updates your package list.--install,--remove,--upgrade,--purge,--downgrade. Each of them do as their names say. The name of the package(s) is mandatory.-i,-r,-u,-p: these are the short options for all except downgrade, who doesn't have one.--safe-upgrade,--full-upgradeare the counterparts toapt-get'supgrade/dist-upgradeandaptitude'ssafe-upgrade/full-upgrade. These doesn't need parameters.- There are several others operations, which can be found in the manual. But, these are the most used by users interested in
aptd. There are options that overlap with whatapt-key,apt-cache,dpkgdo.
apt-get itself doesn't support such methods (to wait for other instances of apt), so aptdcon is the preferred solution to GUI's package managers: USC uses aptd as back-end, same as Synaptic. Other solution is packagekit, but it doesn't support the function that you are looking for (yet).
- 174,089
- 51
- 332
- 407
- 69,112
Since 1.9.11 apt and apt-get have an option that lets you wait for the dpkg locks to be released.
Use the DPkg::Lock::Timeout option to set a timeout, in seconds, for an apt-get command. This example will wait for 60 seconds:
sudo apt-get -o DPkg::Lock::Timeout=60 install packagename
If you set that value to -1, it will keep waiting forever.
sudo apt-get -o DPkg::Lock::Timeout=-1 install packagename
For more information see: Waiting for apt locks without the hacky bash scripts. This option was added to apt-get in February 2020.
One-liner based on Oli's answer:
while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done
- 348
Unfortunately fuser doesn't do a lot for you when you are running in different unprivileged namespace containers like lxc.
Also, aptdcon is not installed by default (at least on 18.04) and backgrounds your task in a queue so you lose serialization. This isn't insurmountable, but it does mean your automation needs to have some way to avoid flock errors in apt when installing aptdcon, and you'll need to have some sort of wait loops for anything you need to serialize after installing packages via aptdcon unless there is some sort of flag for that already.
What does work is flock. This should also work over NFS etc as it uses file system locking in the same way apt does, only with the -w seconds parameter it will wait on your lock instead of throwing an error.
So following the wrapper model, add this as apt-get in /usr/local/bin/ and share away.
This also has the benefit of limiting IO by not allowing parallelism on apt so you can let cron trigger updates at midnight everywhere without beating up the disk.
#!/bin/bash
exec /usr/bin/flock -w 900 -F --verbose /var/cache/apt/archives/lock /usr/bin/apt-get $@
A very nice and simple feature request for apt-get would be a -w flag to switch to a blocking / wait lock.
You could using a polling technique:
$ time (while ps -opid= -C apt-get > /dev/null; do sleep 1; done); \
apt-get -y install some-other-package
- 131
- 4
I made a script which does this:
#!/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
Save the above into /usr/local/sbin/apt-get. apt-get will then wait if another instance is already running.
Alternatively, save it as /usr/local/sbin/apt-wait, example usage:
apt-wait && aptitude
which will run aptitude after the current process holding the lock has exited.
Example run:
First, an
apt-getcommand is run, for example:$ sudo apt-get remove some_packageThen, in another terminal, another command is run:
$ sudo apt-get install some_other_packageIt will wait for the first command to finish then run. Output while waiting:
[/] Waiting for other package managers to finish... 28223 : /usr/bin/apt-get remove some_package