2
\$\begingroup\$

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 >&2
        echo
        continue
    fi
    shopt -s nullglob
    grep -E -I -d skip "$@" $location
    shopt -u nullglob
done
\$\endgroup\$
1
  • \$\begingroup\$ I don't think your edit added much value, and I see no problem with the way I posted my question. \$\endgroup\$ Commented Jun 7, 2014 at 8:43

1 Answer 1

1
\$\begingroup\$

Your questions:

  1. 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)
  2. Yes, just do it once at the start of the script.
  3. No.
  4. 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" >&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
\$\endgroup\$
2
  • \$\begingroup\$ Thank you. I didn't know about globstar! The reason why I was using "$@" was to allow others to pass in other grep options if they so wanted. I will only be using this program with a single regular expression argument. But very powerful to know that grep supports more with just \n char. \$\endgroup\$ Commented Jun 7, 2014 at 8:47
  • \$\begingroup\$ Pity my Bash 3.2.25(1)-release does not support globstar \$\endgroup\$ Commented Jun 7, 2014 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.