3
\$\begingroup\$

I have a bash script I'm trying to distribute to people in my class. The script requires some dependencies to operate, namely wkhtmltopdf and pdfkit. I can install those dependencies with homebrew, but I can't assume users have that. I've written an install/uninstall script for Mac, and my partner made one for Linux, but both of them seem so hacky to me.

I would like a more robust approach for distributing my bash script with its dependencies.

Here is the Mac OS install/uninstall script:

#!/usr/bin/env bash

arg=$1
uninstall="-u"

if [[ ${arg} == *"$uninstall"* ]];
then
    echo "Uninstalling eesubmit..."

    echo "Removing the shortcut..."
    sudo rm /usr/bin/eesubmit
    echo "The eesubmit shortcut has been removed"

    echo "Removing the source code and username file for eesubmit..."
    rm -rf ~/.eesubmit/
    echo "The source code and username file for eesubmit has been removed"

    echo "eesubmit requires other dependencies that may have been installed when eesubmit was installed"
    echo "Some of these dependencies may have already been installed"
    echo "Please review these dependencies and tell us if you want to remove them"

    echo "Do you want to uninstall pdfkit (a Python package that runs on top of wkhtmltopdf)? (y/n)"
    read uninstallPdfkit
    if [ ${uninstallPdfkit} = "y" ];
    then
        pip uninstall pdfkit
    fi

    echo "Do you want to uninstall wkhtmltopdf (a library for converting HTML to PDFs)? (y/n)"
    read uninstallWkhtmltopdf
    if [ ${uninstallWkhtmltopdf} = "y" ];
    then
        brew uninstall pdfkit --force
    fi

    echo "Do you want to uninstall homebrew (a package manager for Mac OS)? (y/n)"
    read uninstallHomebrew
    if [ ${uninstallHomebrew} = "y" ];
    then
        ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"
    fi

    echo "Uninstallation complete!"
else
    echo "Installing homebrew (a package manager for Mac OS)..."
    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    echo "Homebrew installation complete"

    echo "Installing wkhtmltopdf (a library for converting HTML to PDFs)..."
    brew install wkhtmltopdf
    echo "wkhtmltopdf installation complete"

    echo "Installing pdfkit (a Python package that runs on top of wkhtmltopdf)..."
    pip install pdfkit
    echo "pdfkit installation complete"

    echo "Adding a shortcut for eesubmit (this may require your password)..."
    if [ -f ~/.eesubmit/eesubmit/main/submitting.bash ];
    then
        sudo cp ~/.eesubmit/eesubmit/main/submitting.bash /usr/bin/eesubmit
        echo "The shortcut has been created"
        echo "Installation complete!"
    else
        echo "Error: The main eesubmit script is not installed" 1>&2
        echo "Are you sure you cloned the repository correctly?"
        echo "Try running these commands"
        echo "    mkdir -p ~/.eesubmit"
        echo "    cd ~/.eesubmit"
        echo "    git clone https://github.com/DakotaAndMichael/eesubmit.git"
        echo "Then, run this script again"
    fi
fi
\$\endgroup\$
  • \$\begingroup\$ Consider using read -n 1 in a while loop, and use some case to decide to break on [yY]* and [nN]*. \$\endgroup\$ – Arthur2e5 Oct 27 '15 at 12:44
1
\$\begingroup\$

The first thing I can see that should be changed is to add some checks before/after removing files. As it is currently written, the script will break if it goes to remove a file that isn't there. So, for instance, if the uninstall is interrupted halfway (computer unplugged, process killed, whatever), then a subsequent run of the script will throw an error and the last part of the script will never be executed.

For simple checks like these, and for that matter for the single-command if checks you already have, you should just use && and || instead.

if [ ${uninstallPdfkit} = "y" ];
then
    pip uninstall pdfkit
fi

and

[ ${uninstallPdfkit} = "y" ] && pip uninstall pdfkit

are exactly equivalent. Funny thing in this example is that the braces {} are completely unnecessary, as are the double quotes around y, but there should be doublequotes around the variable for safety:

[ "$uninstallPdfkit" = y ] && pip uninstall pdfkit

So instead of:

rm -rf ~/.eesubmit/
echo "The source code and username file for eesubmit has been removed

You should use:

[ -e ~/.eesubmit ] && rm -rf ~/.eesubmit && echo 'The source code and username file for eesubmit has been removed'

This is just scratching the surface; here are some other partial thoughts that could be expanded into further detail:

  • Why do you echo "Uninstallation complete" if the user decides not to uninstall?
  • read has a bunch of flags that would be useful; in particular -p to specify a prompt message. (Use quotes!)
  • Your big error message has only the first line redirected to stderr. Consider using single quotes (since you don't need variable expansion) and just have it include multiple lines with one echo command, then redirect to stderr only once for all of it.
  • You say a shortcut is being created, but you used cp instead of ln -s target linkname. Also you shouldn't need to use sudo at all.

Hope this helps.

\$\endgroup\$

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.