3

I have a bash script which should be re-runnable. It includes

pecl install foo ||
{
    echo "Could not install foo!";
    exit 1;
}

If this runs twice, it errors

pecl/foo is already installed and is the same as the released version 1.2.3
install failed
Could not install foo!

Unlike tools such as apt-get or yum, pecl returns an error code if the module is already installed, whereas I want it to report success.

I could capture and grep stdout/stderr but is there a less hacky way of achieving the same?

1
  • Excellent use of the word 'idempotently' - had to look it up, but it perfectly describes the situation.
    – Argonauts
    Nov 29 '16 at 14:08
5
if ! pecl list | grep foo >/dev/null 2>&1;
then
    pecl install foo ||
    {
        echo_err "Could not pecl install foo";
        exit 1;
    }
fi
0
2

You could discriminate on and mask

the return value from the pecl install foo command with the technique below. It saves the return value from the call, but the 'true' command effectively masks it so the statement returns 0 regardless of the value of retVal.

pecl install foo >/dev/null 2>&1 || { retVal=$?; true; }

But...

this won't help much, because pecl install appears to return a value of 1 regardless of the nature of the failure - a quick test showed that an already installed package or a package not found error will both return 1.

So instead I would simply check if the package is already installed before calling pecl install.

To check if a package is installed I'm assuming the name doesn't include the pecl prefix e.g. jsonc not pecl/jsonc; and that the output from pecl list-all will always start with pecl. To clarify, lets say we are working with package jsonc:

 pkg="jsonc"
 pecl list-all | grep "$pkg"

Will result in this output (assuming its installed):

pecl/jsonc                    1.3.10 JavaScript Object Notation

To avoid falsely declaring a package installed the regex used with grep leverages that output format - if we simply matched on package name, a match on jsonc would also be a match for jsoncde (not a real package). So the regex is looking for the start of the line to be pecl/ followed by the package name, with a space after package name : "^pecl/$pkg "

You may need to modify that regex based on the possible package names in use.

#!/bin/bash

if [ $# -lt 1 ]; then
  echo "Package name must be passed as sole argument"
  exit 2
else
  pkg="$1"
fi

if pecl list-all | grep -Eq "^pecl/$pkg " ; then
  echo "package $pkg is already installed"
  exit 0
else # not installed
  if pecl -q install "$pkg" >/dev/null ; then
    echo "Package $pkg installed successfully"
    exit 0
  else
    echo "Error occurred during installation of $pkg"
    exit 1
  fi
fi

If the pecl list-all | grep ... command misbehaves when checking for installed packages, you may also be able to use the command pecl info $pkg. I didn't look into it much, but it seems to return a 0 (and a bunch of info to stdout) when the package being queried is installed, and returns a 1 when the package isn't installed (and an error to stderr).

1

I worked around this by simply uninstalling the module, as if the module does not exist and you do an uninstall, it returns 0, and since in my case this is a rare job, the extra reinstall does not matter.

2
  • Yes, very good thinking. Similar to pecl uninstall you can also use pecl upgrade which has the benefit to not uninstall: pecl upgrade <package> (Note: works only if you don't specify the package version, so might not always be an option)
    – hakre
    Feb 1 at 16:22
  • And as seeing it, instead of uninstalling first you can use the -f option to force the install.
    – hakre
    Feb 1 at 16:35
0

Why not just pretend to install (-p option) and then install if it's fine?

set -e
if pecl install -p -- "$PACKAGE"; then
  pecl install -- "$PACKAGE"
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.