54

I have a text file with lots of package names.

package1
package2

# comment
# installing package3 because it was needed for...
package 3

package 4

How can I mass install all packages inside the text file without removing the comments?

James Mitch
  • 1
  • 10
  • 23
  • 45
  • This solution allows you to use comments: https://www.monolune.com/installing-apt-packages-from-a-requirements-file/ – Flux Feb 17 '18 at 19:44

6 Answers6

52

Something along these lines ought to do the trick.

apt-get install $(grep -vE "^\s*#" filename  | tr "\n" " ")

The $(something) construction runs the something command, inserting its output in the command line.

The grep command will exclude any line beginning with a #, optionally allowing for whitespace before it. Then the tr command replaces newlines with spaces.

andol
  • 6,713
  • 2
  • 33
  • 31
  • 5
    `tr` will fail with multi-byte end-of-line sequences (think `\r\n`), why don't you use `xargs`? – Ivan Anishchuk Feb 17 '16 at 16:20
  • You can also ignore trailing comments ```apt-get install $(grep -vE "^\s*#" Aptfile | sed -e 's/#.*//' | tr "\n" " ")``` – Jamie Cook Sep 16 '20 at 23:46
  • Just wondering, how difficult would it be to create a "dummy package", which is empty except for metadata. I know that the `depends` argument in the metadata is used by apt / apt-get commands as input, just as if you were to type `sudo apt install `. Seems like it would be a great solution to this issue. I would post myself, but I am not sure about the specifics. maybe someone a bit more experienced at package creation / handling could pick up the torch here? – Nate T Jun 18 '21 at 14:10
  • Can this done one by one? – alper Jul 23 '21 at 11:42
20

The following command is a (slight) improvement over the alternative because sudo apt-get install is not executed when the package list is empty.

xargs -a <(awk '! /^ *(#|$)/' "$packagelist") -r -- sudo apt-get install

Note that the -a option reads items directly from a file instead of standard input. We don't want to pipe a file into xargs because stdin must remain unchanged for use by apt-get.

Six
  • 812
  • 7
  • 7
  • `xargs` is the right way to do this. Trick with `<()` is neat. – Ivan Anishchuk Feb 17 '16 at 16:19
  • 1
    That's indeed the better solution because `xargs` makes sure `ARG_MAX ` is not reached. – phk Jun 12 '16 at 11:45
  • It has to be `xargs -a $(awk '/^\s*[^#]/' "$packagelist") -r -- sudo apt-get -y install`, not "`<(`" but "`$(`" and the option `-y` for apt-get would be a good idea. –  Jul 11 '16 at 20:19
  • 1
    Process substitution redirects the output of `awk` into a file descriptor for `xargs -a` to read from. So you definitely want `<(` and not `$(`. Just try it and you'll see what I mean. If the command is to be running unattended and you already know exactly what's going to be installed then sure, they `-y` flag is a good idea. – Six Jul 12 '16 at 03:01
  • Would it be still run if one of the packages in the list crashes to be installed? – alper Mar 06 '22 at 14:42
7

Given a package list file package.list, try:

sudo apt-get install $(awk '{print $1'} package.list)
cmcginty
  • 5,768
  • 7
  • 34
  • 33
3

I use this simple solution:

grep -vE '^#' file.txt | xargs sudo apt install -y

grep finds all lines that don't start with a # and gives them as arguments to sudo apt install.

Syd Lambert
  • 131
  • 3
2

Well, here's my solution to install a list of packages I have for fresh install:

sudo apt install -y $(grep -o ^[^#][[:alnum:].-]* "filename")

In a bash function :

aptif () {
    sudo apt install -y $(grep -o ^[^#][[:alnum:].-]* "$1")
}

grep explanation :

  • -o keep only the part of line that matches the expression
  • ^[^#] anything that does not start with a #
  • [[:alnum].-]* a sequence of letters, numbers, . and -
Dag Høidahl
  • 103
  • 4
Biggybi
  • 585
  • 3
  • 11
2

Inspired by the accepted answer here and this answer on removing comments:

apt-get install $(grep -o '^[^#]*' filename)