23

I have a simple requirement. I want to define several variables that will correspond to any number of given packages I want to install via a shell script.

Sample code below:

MISC="shutter pidgin"
WEB="apache2 mongodb"

for pkg in $MISC $WEB; do
    if [ "dpkg-query -W $pkg | awk {'print $1'} = """ ]; then
        echo -e "$pkg is already installed"
    else
        apt-get -qq install $pkg
        echo "Successfully installed $pkg"
    fi
done

Everything kinda works, but the logic seems flawed because it's not reliably installing the packages I want. It either says they've been installed already or it's trying to install packages that have already been installed previously.

I've also been trying with command -v or the following:

if [ "dpkg -l | awk {'print $2'} | grep --regexp=^$pkg$ != """ ]; then

And even with the -n and -z flags to check if the returned string was empty. Pretty sure I'm missing some good sense here.

Do you have any idea what I could do to make sure a package is actually installed or not?

Thanks!

1
  • What harm there is, if you call apt-get install for installed packages, too? You could just call apt-get install $MISC $WEB.
    – jarno
    Apr 22 '15 at 20:25
18

Essentially you only need to replace the if condition with

if dpkg --get-selections | grep -q "^$pkg[[:space:]]*install$" >/dev/null; then

It is not possible to use dpkg-query, because it returns true also for packages removed but not purged.

Also I suggest to check the exit code of apt-get before giving the successful message:

if apt-get -qq install $pkg; then
    echo "Successfully installed $pkg"
else
    echo "Error installing $pkg"
fi
5
  • What does &> do?
    – Taymon
    Jul 12 '13 at 15:10
  • @Taymon: redirect both stdin and stderr to the given file (/dev/null in this case), because we don't need the output, only the exit code. It only works in bash (the first line of the script has to be #!/bin/bash) otherwise use >/dev/null 2>&1.
    – enzotib
    Jul 12 '13 at 15:14
  • @Taymon: I changed the logic, because I found a flaw in the preceding solution.
    – enzotib
    Jul 12 '13 at 15:27
  • Beware: if using bash and the pipefail option is set, then the grep -q can generate Heisenbugs. Basically, grep exits before dpkg finishes writing (you want a 0 exit status in that case) so dpkg fails writing to the pipe (which instead generates a non-0 exit status). Either make sure pipefail is not set, or abandon the (probably tiny) efficiency gains of the "-q" option.
    – Ron Burk
    Apr 30 '15 at 17:50
  • This answer internally produces as many lines as there are packages known by the system (2870 on my machine), to be then filtered by grep (thus potentially slow), while @jarno's answers uses dpkg-query that returns only one line (potentially faster). On a Raspberry Pi 3, the one based on dpkg-query appears (though inconsistently) faster. Jan 3 '20 at 18:24
4

You can test it by dpkg-query:

if dpkg-query -W -f'${Status}' "$pkg" 2>/dev/null | grep -q "ok installed"; then

Note that * and ? are wildcards, if they appear in $pkg. I guess dpkg-query may print "reinst-required installed" instead of "ok installed", if package is broken and needs to be reinstalled by command apt-get install --reinstall which can be used to install new packages as well.

0
#to check package is installed or not without distribution dependency
#!/bin/bash
read -p "Package Name: " pkg
which $pkg > /dev/null 2>&1
if [ $? == 0 ]
then
echo "$pkg is already installed. "
else
read -p "$pkg is not installed. Answer yes/no if want installation_ " request
if  [ $request == "yes" ]
then
yum install $pkg
fi
fi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.