Grep to search for array of location targets Please review my short 65 lines of code:


Is there a way to avoid eval (security)?
Is there a better way or place to use shopt -s nullglob?
Does shopt -u nullglob always need to be unset through each iteration?
Is there a smarter way to define a list of locations?




#!/bin/bash
if [ $# -eq 0 ]
then
    echo specify at least a regular expression pattern
    exit 1
fi
# make an array. Use 'single quotes'
SEARCH_LOCATIONS=(
'$BIN_HOME/*'
'$PBIN_HOME/*'
'$PBIN_HOME/lib/*'
'$SH_HOME/* '
'$SH_HOME/*/*'
'$PROD_HOME/psp/*.psp'
'$PROD_HOME/sql/*.sql'
'$PROD_HOME/sql/*/*.sql'
'$PROD_HOME/rpt/[A-Z]*.rpt'
'$PROD_HOME/script/*'
'$PROD_HOME/fmb/[A-Z]*.pll'
'$PROD_HOME/xml/[A-Z]*.pld'
'$PROD_HOME/fmb/[A-Z]*.mmb'
'$PROD_HOME/xml/[A-Z]*.xml'
'$PROD_HOME/cgi/*'
'$ELETTUS_HOME/*'
'$ELETTUS_HOME/*/*'
'$PROD_HOME/guicommon/*'
)
for LOCATION in ${SEARCH_LOCATIONS[@]}
do
    # ---- evaluate array contents
    echo --------------------------------------------------------------------------------
    echo "$LOCATION"
    echo
    eval declare location=$LOCATION
    if [[ $location != /u2/* ]]
    then
        echo $LOCATION is not a part of Lettus system &gt;&amp;2
        echo
        continue
    fi
    shopt -s nullglob
    grep -E -I -d skip "$@" $location
    shopt -u nullglob
done
Your questions:


Yes. Use double quotes and then drop the eval (this will also cause the script to show /path/to/prod/home instead of $PROD_HOME)
Yes, just do it once at the start of the script.
No. 
Depends on your requirements.


Other issues: 


Using multiple regex can be specified but won't work
Using paths with spaces doesn't work


Keeping most of your current functionality, I probably do something like this:

#!/bin/bash
shopt -s nullglob

# Allow using ** to recursively expand globs
shopt -s globstar

# Don't do any field splitting
IFS=''

if (( ! $# ))
then
    echo "specify at least one regex"
    exit 1
fi

locations=(
  "$BIN_HOME/*"
  "$PBIN_HOME/**"
  "$SH_HOME/**"
  "$PROD_HOME/psp/*.psp"
  "$PROD_HOME/sql/*.sql"
  "$PROD_HOME/sql/*/*.sql"
  "$PROD_HOME/rpt/[A-Z]*.rpt"
  "$PROD_HOME/script/*"
  "$PROD_HOME/fmb/[A-Z]*.pll"
  "$PROD_HOME/xml/[A-Z]*.pld"
  "$PROD_HOME/fmb/[A-Z]*.mmb"
  "$PROD_HOME/xml/[A-Z]*.xml"
  "$PROD_HOME/cgi/*"
  "$ELETTUS_HOME/**"
  "$PROD_HOME/guicommon/*"
)

# Quote all variables unless you specifically can't
for location in "${locations[@]}"
do
    echo "--------------------------------------------------------------------------------"
    # Printf generally works better for arbitrary data, though this is unlikely to be an issue in your case
    printf "%s\n" "$location"
    echo
    if [[ "$location" != /u2/* ]]
    then
        printf "%s is not a part of Lettus system\n" "$location" &gt;&amp;2
        echo
        continue
    fi

    # grep can accept multiple patterns separated by line feeds
    # Add a dummy file so non-matching globs won't cause grep to read from stdin
    # Here we want $location to glob expand, so no quotes (IFS was set earlier to prevent field splitting)
    grep -E -I -d skip "$(printf "%s\n" "$@")" /dev/null $location
done