Shell script to create a boilerplate proyect This is actually my first shell script and just by looking at it I know this can look better. I use Elixir and Phoenix for most of my projects and I started using Zurb Foundation 6, changing from Bootstrap to Foundation is kinda tiresome sometimes so I decided to make a script to automate this process.

This is the script

#!/bin/sh

JAVI_PATH="$HOME/.javi"

name=${1}
path=${2}
rest=${@:3}

### Funcs
get_files() {
  cd ${name}/assets

  echo "Installing extra dependencies"
  npm install --save jquery sass-brunch foundation-sites normalize-scss

  echo "Getting files to replace"

  get_brunch
  get_scss

  cd ../..

  echo "Got and replaced files on 'assets' folder"

  # Replaces content from 'app.html.eex' file
  get_apphtml
}

get_brunch() {
  rm -rf brunch-config.js &amp;&amp; \
  wget https://raw.githubusercontent.com/aguxez/javi/master/brunch-config.js
}

get_scss() {
  cd css &amp;&amp; \
  rm -rf app.css phoenix.css &amp;&amp; \
  wget https://raw.githubusercontent.com/aguxez/javi/master/app.scss
}

get_apphtml() {
  cd lib/${name}_web/templates/layout &amp;&amp; \
  rm -rf app.html.eex &amp;&amp; \
  wget https://raw.githubusercontent.com/aguxez/javi/master/app.html.eex

  # Here we replace the title with the 'name' variable
  uppercased_name=`sed -e "s/\b\(.\)/\u\1/g" &lt;&lt;&lt; "${name}"`
  eval sed -i -e 's/CHANGE_TITLE/${uppercased_name}/g' app.html.eex
}

## If installing
INSTALL=0
if [ "$1" = "install" ] ; then
  INSTALL=1
fi

if [ "$INSTALL" = 1 ] ; then
  if [ ! -d "$JAVI_PATH" ] ; then
    mkdir $JAVI_PATH &amp;&amp; \
    curl -sSL https://raw.githubusercontent.com/aguxez/javi/master/javi &gt; $JAVI_PATH/javi &amp;&amp; \
    chmod +x $JAVI_PATH/javi

    echo "Javi has been configured"
  fi
elif [ -z "${name}" ]; then
  echo "Please give a name to your project"
elif [[ -d ${path} ]]; then
  echo "Creating project in ${path}"
  cd ${path}
  mix phx.new ${name} ${rest}
  # Runs function
  get_files
else
  mix phx.new ${name} ${rest}
  # Function
  get_files
fi


Maybe I could think of a better way of handling arguments here

if [ "$INSTALL" = 1 ] ; then
  if [ ! -d "$JAVI_PATH" ] ; then
    mkdir $JAVI_PATH &amp;&amp; \
    curl -sSL https://raw.githubusercontent.com/aguxez/javi/master/javi &gt; $JAVI_PATH/javi &amp;&amp; \
      chmod +x $JAVI_PATH/javi

    echo "Javi has been configured"
  fi
elif [ -z "${name}" ]; then
  echo "Please give a name to your project"
elif [[ -d ${path} ]]; then
  echo "Creating project in ${path}"
  cd ${path}
  mix phx.new ${name} ${rest}
  # Runs function
  get_files
else
  mix phx.new ${name} ${rest}
  # Function
  get_files
fi


But I'm not sure what I can change or HOW can I change it.
I'm not a shell expert, but I think I could help a bit. The most relevant tips I could give are:

1. Quote your expansions

Unquoted expansions are the most common source of bugs and security issues in shell scripts. There are cases where it's safe to leave expansions unquoted (as in foo=$bar) but, unless extremely necessary, it's preferable to double-quote every expansion you have.

2. Prefer $(command) over `command`

The $(command) syntax is POSIX-compliant and it can be easily nested.

3. Be consistent

You mix different styles in your code.


Quotes: There are both quoted and unquoted expansions. As I said before, prefer quoted expansions unless you are explicitly looking for side effects like word splitting.
Braces: When doing parameter expansions, sometimes you use braces, sometimes not. Braces are optional in most of the simple substitutions but they are useful when you have two-digit positional parameters or to avoid ambiguity. I personally prefer to always put my parameters within braces.
[ and [[: If portability is a concern, use [. If not, use [[.


4. Be careful with eval

Avoid it if you are not completely sure of what you are doing.

5. Arrays

I suppose you use the rest variable to save your remainder arguments. The problem is that you are saving them as a single string and you are relying on word splitting to handle it.

If your shell support arrays, this would be a safer and cleaner solution:

rest=( "${@:3}" )
...
mix phx.new "${name}" "${rest[@]}"


6. Shell builtins

If available, prefer them over external commands.

For example, if you use a recent version of bash and portability is not a big deal, you could rewrite uppercased_name=`sed -e "s/\b\(.\)/\u\1/g" &lt;&lt;&lt; "${name}"`  as uppercased_name="${name^}"

7. Arguments parsing

First, I recommend you to read the Utility Conventions chapter of the POSIX specification. It's useful to understand how argument syntax works in standard utilities and serves as a guide to the rules you should follow when you create some program.

The goal is to make sure your script follows the program [options] [operands] syntax. I mean, instead of rely on the position of arguments to assign variables (as in name=${1}), your script should parse --name foo (or something similar) to change the value of the name variable.

There are basically two approaches:


Using utilities like getopts


Pros:


getopts is a POSIX builtin command.
It can handle things like -abc without effort.
Easy and intuitive.

Cons:


Can't handle long options.


Writing your own parser


Pros:


Full control of what and how it is parsed.
Handling of long options.
Could implement non-standard syntax.

Cons:


It's harder to handle things like -abc and edge cases.




Greg's and Bash Hackers' (1, 2) wikis have tutorials for both approaches, so check them out.

I've also written an example script to show how to do manual parsing and colored output: