Reliably check if a package is installed or not

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!


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

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.