2

I'm building a bash script that uses wget to GET information from a server using a REST api. I'm using getopts to parse options given to the script and then using an if statement to redirect the script correctly based on the options given. The if goes to the main body of the script (ie the wget call), the elif prints the help menu, and the else prints an error message. However my elif appears to be acting as an else statement. When I run:

>./jira -h

I get the proper response, i.e. the help menu:

----------jira options----------
Required:
-d [data/issueID]
-u [username] -> [username] is your JIRA username
-p [password] -> [password] is your JIRA password

Optional:
-q -> quiet, i.e. no output to console
-h -> help menu

However, when I run something that should give me the error message I get help menu instead:

>./jira -u jsimmons

----------jira options----------
Required:
-d [data/issueID]
-u [username] -> [username] is your JIRA username
-p [password] -> [password] is your JIRA password

Optional:
-q -> quiet, i.e. no output to console
-h -> help menu 

My script is below:

#!/bin/bash

#using getopts to parse options
while getopts ":hqd:u:p:" opt; do
        case $opt in
                h)
                        help="true"
                        ;;      
                q)
                        quiet="true"
                        ;;      
                d)
                        data=$OPTARG
                        ;;
                u)
                        username=$OPTARG
                        ;;
                p)
                        password=$OPTARG
                        ;;
                \?)
                        echo "Invalid option: -$OPTARG" >&2
                        ;;
                :)
                        echo "Option -$OPTARG requires an argument." >&2
                        ;;
        esac
done

#check if required options have been set
if [[ -n $data && -n $username && -n $password ]]; then 

        wget -q \
            --http-user=$username \
            --http-passwd=$password \
            --header="Content-Type: application/json" [URI]


        #placing issue info into variable

        response=$(< $data) 


        #using heredoc to run python script
        #python script uses regular expressions to find the value of the field 
        #customfield_10701 ie the branch version 

        output=$(python - <<EOF
import re

matchObj = re.search(
     r'(?<=customfield_10701":").*(?=","customfield_10702)',
     '$response',
     re.I
)
if(matchObj):
        print(matchObj.group())

EOF
)


        #writes branch version in .txt file

        echo $output>branchversion.txt 


        #prints the branch version if the quiet option hasn't been set 
        if [ -z $quiet ]; then
                echo "-------------------------------------------" 
                echo ""
                echo "The branch version for issue $data is:"
                cat branchversion.txt
                echo ""
        fi

        #removes file that wget creates containing all data members for the issue

        rm $data
elif [ -n $help ]; then 
        #if help option has been set    
        echo "" 
        echo "----------jira options----------"
        echo "Required:"
        echo "-d [data/issueID]"
        echo "-u [username] -> [username] is your JIRA username"
        echo "-p [password] -> [password] is your JIRA password"
        echo ""
        echo "Optional:"
        echo "-q -> quiet, i.e. no output to console"
        echo "-h -> help menu"
        echo ""
        #http GET data members for issue
else
        #if not all required options or help have been set      
        echo "Error: Missing argument(s)"
        echo "Usage: ./jira [option(s)] -d [data] -u [username] -p [password]"
        echo "" 
        echo "Try: ./jira -h for more options"
fi
Void
  • 25

3 Answers3

2

the -n option checks if a string is non-zero length.

if [ ... ]; then #posix compliant condition tests

if [[ ... ]]; then #extended condition tests

It seems the extended condition tests work differently than the posix ones.

> if [ -n $unsetVar ];then echo yes ; fi
yes
>

> if [ -n "$unsetVar" ];then echo yes ; fi
>

> if [[ -n $unsetVar ]];then echo yes ; fi
>

either use the extended conditions for both [[ ... ]] or wrap your variable in quotations. Currently your elif statement is always true.

Centimane
  • 4,490
2

The fundamental problem is that you omitted double quotes around a variable substitution. When you write $help, in most contexts, that doesn't mean “take the value of help”, that means “take the value of help, interpret it as a whitespace-separated list of wildcard patterns, and replace each pattern by the list of matching file names if at least one file name matches”.

When help is empty, [ -n $help ] expands to the list of three words [, -n, ], since the result of split+glob on an empty string is an empty list of words. When there is a single word between the brackets, the condition is true unless the word is empty. -n isn't empty so the condition is true.

The fix is to write [ -n "$help" ]: when help is empty, this expands to the list of four words [, -n, empty word, ]. This applies the -n operator to the empty word and the condition is false.

An alternative solution is to use [[ -n $help ]]. The double brackets are special syntax, with different parsing rules (whereas single brackets are an ordinary command with a funny name). Inside double brackets, you can leave off double quotes around variable substitutions in most contexts, the exception being the right-hand side of =, ==, != and =~. You can write [[ -n "$help" ]] if you like, so you don't need to remember the exceptions where leaving off the double quotes is allowed: just remember to always put double quotes around variable substitutions (and similarly around command substitutions: "$(foo)").

1
  1. you don't even need an if/elif here. Instead of -h setting help=true, create a usage() function which prints usage help and exits the script, the -h option can then just call that function.

  2. why are you using embedded python code to do a job that could more easily be done with grep or sed or awk? or jq

  3. your code won't work anyway. You seem to have become confused about what $data holds. According to the help and the getopts case statement, $data holds the data/issueID....but it looks like you're assuming it holds the contents of a URL fetched with wget.

    In fact, it looks like you aren't even using that -d data/issueID anyway...perhaps you should include it in a query string in the wget request?

    or is -d supposed to have a filename as an argument? From the help message, it doesn't seem likely.


Anyway, here's a version that fixes most of the problems (and, IMO, greatly improves the script's readability):

#!/bin/bash

URI='...whatever...'

usage() {
# print optional error message and help message and exit.

[ -z "$*" ] || printf "%s\n\n" "$*"
# or, if you don't want help printed with every error message:
# [ -z "$*" ] || printf "%s\n\nUse -h option for help.\n" "$*" && exit 1

cat >&2 <<__EOF__
----------jira options----------
Required:
  -d [data/issueID]
  -u [username] -> [username] is your JIRA username
  -p [password] -> [password] is your JIRA password

Optional:
  -q -> quiet, i.e. no output to console
  -h -> help menu
__EOF__

exit 1
}

quiet=''

#using getopts to parse options
while getopts ":hqd:u:p:" opt; do
  case $opt in
     h) usage ;; 
     q) quiet='true' ;;     
     d) data="$OPTARG" ;;
     u) username="$OPTARG" ;;
     p) password="$OPTARG" ;;
    \?) usage "Invalid option: -$OPTARG" ;;
     :) usage "Option -$OPTARG requires an argument." ;;
  esac
done

# check for required options

[ -z "$data" ]     && usage '-d is required'
[ -z "$username" ] && usage '-u is required'
[ -z "$password" ] && usage '-p is required'

# your embedded python overkill could easily be done with `sed`
# or `awk`.   or maybe `jq` as the wget output is json.
# but without a sample of the input I can't provide a guaranteed
# working example.  The awk script below is a first attempt at
# extracting the value of `customfield_10701`

wget -q --http-user="$username" \
        --http-passwd="$password" \
        --header='Content-Type: application/json' \
        -O -
        "$URI" |
  awk -F': ' '/"customfield_10701"/ {
                gsub(/[",]/,"",$2);
                print $2
              }' > branchversion.txt

# alternatively, if you have `jq` installed, try piping wget's
# output through something like `jq -r .customfield_10701` instead of awk.

#prints the branch version if the quiet option hasn't been set
if [ -z "$quiet" ]; then
  echo "-------------------------------------------"
  echo ""
  echo "The branch version for issue $data is:"
  cat branchversion.txt
  echo ""
fi
cas
  • 78,579