Is there a difference between downloading / installing a list of packages and downloading each package by its own?

Given a list of dev packages (e.g. pkgs="python3.5-dev python3-tk"), would there be a difference between running

sudo apt-get install $pkgs 

vs.

for p in $pkgs; do sudo apt-get install $p; done

I'm asking mostly dependencies-topo-sort related, i.e. will there be different packages downloaded and installed in either way


Solution 1:

Yes, different packages can be selected to satisfy dependencies. Running 1 command that specifies N packages to be installed will sometimes have a different effect from running N commands each of which specifies 1 package to be installed, even when the actual packages specified are the same in each case.

The main practical difference is probably the one karel has described. However, the effects can differ even when all operations succeed, due to different dependency resolution.

The reason is as you broadly surmised:

I'm asking mostly dependencies-topo-sort related

More specifically, this happens because there can be more than one alternative that satisfies a dependency.

Abstract Explanation

Suppose you wish to install package a, which depends on a virtual package v, and v can be satisfied by package b or package c and in no other way. Suppose b does not depend on c, c does not depend on b, and none of those packages are installed already. Now suppose you run:

sudo apt-get install a

Then APT will select b or c to satisfy the dependency. Which one it picks is the result of a computation it performs that may be affected by what packages you have installed, what packages are available, what version of APT you are using, and how it is configured. But when all these conditions are the same, the decision will be made the same each time.

Suppose without loss of generality that it picks b. Then a and b are installed. Suppose you then run:

sudo apt-get install c

After that, a, b, and c are all installed. That is, all three packages were installed as a result of running:

sudo apt-get install a
sudo apt-get install c

In contrast, suppose you were to run this command instead of those:

sudo apt-get install a c

Then, since a's dependency on v is satisfied by c, b is not installed. That is, only a and c, but not b, are installed as a consequence of running:

sudo apt-get install a c

Concrete Example

You can find concrete examples of this by running apt or apt-get with the -s option, which causes it to simulate your action rather than performing it. (Just remember that you cannot fully confirm them just with apt-get -s/apt -s, because earlier apt-get -s/apt -s commands do not affect later ones, since they do not affect anything, since they are only simulations.)

Running sudo apt-get install jedit on my Ubuntu 16.04 LTS system would install several OpenJDK 8 packages:

ek@Io:~$ apt -s install jedit
NOTE: This is only a simulation!
      apt-get needs root privileges for real execution.
      Keep also in mind that locking is deactivated,
      so don't depend on the relevance to the real current situation!
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  ca-certificates-java default-jre default-jre-headless java-common java-wrappers openjdk-8-jre openjdk-8-jre-headless
Suggested packages:
  default-java-plugin icedtea-8-plugin fonts-wqy-zenhei fonts-indic
The following NEW packages will be installed:
  ca-certificates-java default-jre default-jre-headless java-common java-wrappers jedit openjdk-8-jre
  openjdk-8-jre-headless
0 upgraded, 8 newly installed, 0 to remove and 11 not upgraded.
Inst java-wrappers (0.1.28 Ubuntu:16.04/xenial [all])
Inst ca-certificates-java (20160321ubuntu1 Ubuntu:16.04/xenial-updates [all]) []
Inst java-common (0.56ubuntu2 Ubuntu:16.04/xenial [all]) []
Inst openjdk-8-jre-headless (8u222-b10-1ubuntu1~16.04.1 Ubuntu:16.04/xenial-updates, Ubuntu:16.04/xenial-security [amd64])
Inst default-jre-headless (2:1.8-56ubuntu2 Ubuntu:16.04/xenial [amd64])
Inst openjdk-8-jre (8u222-b10-1ubuntu1~16.04.1 Ubuntu:16.04/xenial-updates, Ubuntu:16.04/xenial-security [amd64])
Inst default-jre (2:1.8-56ubuntu2 Ubuntu:16.04/xenial [amd64])
Inst jedit (5.3.0+dfsg-1 Ubuntu:16.04/xenial [all])
Conf java-wrappers (0.1.28 Ubuntu:16.04/xenial [all])
Conf java-common (0.56ubuntu2 Ubuntu:16.04/xenial [all])
Conf ca-certificates-java (20160321ubuntu1 Ubuntu:16.04/xenial-updates [all])
Conf openjdk-8-jre-headless (8u222-b10-1ubuntu1~16.04.1 Ubuntu:16.04/xenial-updates, Ubuntu:16.04/xenial-security [amd64])
Conf default-jre-headless (2:1.8-56ubuntu2 Ubuntu:16.04/xenial [amd64])
Conf openjdk-8-jre (8u222-b10-1ubuntu1~16.04.1 Ubuntu:16.04/xenial-updates, Ubuntu:16.04/xenial-security [amd64])
Conf default-jre (2:1.8-56ubuntu2 Ubuntu:16.04/xenial [amd64])
Conf jedit (5.3.0+dfsg-1 Ubuntu:16.04/xenial [all])

If I told APT to install both jedit and openjdk-9-jre by running sudo apt-get install jedit openjdk-9-jre, then it wouldn't install OpenJDK 8 packages, because jedit's dependency on a Java runtime is satisfied by OpenJDK 9 packages:

ek@Io:~$ apt -s install jedit openjdk-9-jre
NOTE: This is only a simulation!
      apt-get needs root privileges for real execution.
      Keep also in mind that locking is deactivated,
      so don't depend on the relevance to the real current situation!
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  ca-certificates-java java-common java-wrappers libatk-wrapper-java libatk-wrapper-java-jni openjdk-9-jre-headless
Suggested packages:
  default-jre icedtea-8-plugin fonts-indic
The following NEW packages will be installed:
  ca-certificates-java java-common java-wrappers jedit libatk-wrapper-java libatk-wrapper-java-jni openjdk-9-jre
  openjdk-9-jre-headless
0 upgraded, 8 newly installed, 0 to remove and 11 not upgraded.
Inst java-wrappers (0.1.28 Ubuntu:16.04/xenial [all])
Inst ca-certificates-java (20160321ubuntu1 Ubuntu:16.04/xenial-updates [all]) []
Inst java-common (0.56ubuntu2 Ubuntu:16.04/xenial [all]) []
Inst openjdk-9-jre-headless (9~b114-0ubuntu1 Ubuntu:16.04/xenial [amd64])
Inst libatk-wrapper-java (0.33.3-6 Ubuntu:16.04/xenial [all])
Inst libatk-wrapper-java-jni (0.33.3-6 Ubuntu:16.04/xenial [amd64])
Inst openjdk-9-jre (9~b114-0ubuntu1 Ubuntu:16.04/xenial [amd64])
Inst jedit (5.3.0+dfsg-1 Ubuntu:16.04/xenial [all])
Conf java-wrappers (0.1.28 Ubuntu:16.04/xenial [all])
Conf java-common (0.56ubuntu2 Ubuntu:16.04/xenial [all])
Conf ca-certificates-java (20160321ubuntu1 Ubuntu:16.04/xenial-updates [all])
Conf openjdk-9-jre-headless (9~b114-0ubuntu1 Ubuntu:16.04/xenial [amd64])
Conf libatk-wrapper-java (0.33.3-6 Ubuntu:16.04/xenial [all])
Conf libatk-wrapper-java-jni (0.33.3-6 Ubuntu:16.04/xenial [amd64])
Conf openjdk-9-jre (9~b114-0ubuntu1 Ubuntu:16.04/xenial [amd64])
Conf jedit (5.3.0+dfsg-1 Ubuntu:16.04/xenial [all])

If I installed jedit and openjdk-9-jre with two separate sudo apt-get install commands, issued in that order, then I would get both OpenJDK 8 and OpenJDK 9 packages.

Solution 2:

If even one package from a very long list of packages can't be installed, then the execution of the command will stop and it will print an informative error message. It can also be frustrating to install a long list of packages in one batch because some of the packages will require interactive user input in the terminal in order to be installed successfully. If you make a mistake in the user input then you will have to restart the entire installation process from the beginning.

It's difficult to get the command to execute successfully until the end unless you split the initial long list of packages into groups of 25-40 packages and then install the packages with apt one group at a time. This doesn't take much extra time, and makes installing all of the packages easier to manage.

Just remember one more thing. sudo apt install locks the administrative directory (/var/lib/dpkg/) when it is running, so don't run any other process that requires root privileges until apt completely finishes processing. For example if you are installing packages with apt from the terminal, don't try to install other snap packages from the terminal in a new tab or window until apt finishes processing.