0

I wrote I ‘quick’ script, and then thought of adding a “—nomail” option that would shut off my mail switch (of which the syntax is piped in from echo).

I thought this would be easy but I think, fundamentally, I did not structure the original script well so, this ‘option’ is killing me.

Very new to bash… THANKS!

MY WONDERFUL WORKING SCRIPT!:

 ## Current threshold value setting
 THRESHOLD="80"                                      

 ## Input Files and Mailx
 LOCATIONS="fsIn.txt"                                               ## Edit "fsIn.txt" to add or change filesystems
 TOLIST="$(cat toList.txt | tr -s '\n' ' ' )"                       ## Sets who receives email if an error condition exists
 REPLYTO="$(cat replyTo.txt | tr -s '\n' ' ' )"                     ## sets the reply to email addresses
 FROM=”SCRIPT TEAM"                             ## Sets the "from" address on error emails
 SUBJECT="$HOST: ! STORAGE LEVEL MET OR EXCEEDED !"         ## Sets the subject line
 ############

 for i in $(cat $LOCATIONS)

 do

 ## Main df pipeline to return usage percentage
 CAP=$(df -PH --no-sync "$i" | awk 'NR>1'| awk {'print $5'} | sed 's/.$//')

for i in $(cat $LOCATIONS)                  #Several different file system locations are ‘catted’ in here .  E.g.  /dev

do

## Main df pipeline to return usage percentage to stdout and piped to mailx
CAP=$(df -PH --no-sync "$i" | awk 'NR>1'| awk {'print $5'} | sed 's/.$//')
        if [ $CAP -ge $THRESHOLD ]
            then
           (echo                               
            echo "---------- CAPACITY TEST FAILED ---------- "
            echo -n "  SYSTEM NAME: " ; uname -n
            echo -n "  USER DETAIL: " ; whoami
            echo "  TEST AREA:   $i "
            echo "  USED SPACE:  $CAP% "
            echo "  THRESHOLD:   $THRESHOLD% "
            echo "  !!!!!! THRESHOLD EXCEEDED !!!!!! ") | tee >(mailx -s "$SUBJECT" -r "$FROM" -S replyto="$REPLYTO" "$TOLIST")
            echo

        else
            echo
            echo "++++++++++ CAPACITY TEST PASSED ++++++++++ "
            echo "  TEST AREA:   $i "
            echo "  USED SPACE:  $CAP% "
            echo "  THRESHOLD:   $THRESHOLD% "
            echo " !!! SUCCESS SUCCESS SUCCESS SUCCESS !!! "
            echo
    fi

done

exit 0

This works perfectly! But I haven’t a clue how to restructure this to include a –nomail option. It seems ‘case’ might be prudent but I am lost here.

Any thoughts?

Thanks so much!

  • I just edited your question so that all the code was formatted correctly, but now it seems that there's a missing done somewhere in the script (for the first for loop) - is your code one single script or are you showing two different alternative versions of the for loop? – cas Mar 7 '18 at 3:11
  • Clean indentation makes code readable. What shall be affected by the -nomail option? Can't you show it on a smaller exampler? Which Interpreter is used, zsh, bash, ... - heterogenous? – user unknown Mar 7 '18 at 4:51
0

One easy answer is twofold:

  • Set a flag such as send_email to true, and either unset it or set it to false if your email suppressing argument is present.
  • Compose your email in advance by assembling a variable or temporary file which would be the output, and then punting the actual sending of mail to a function call:

For example:

send_email="true"
handle_email() {
    echo "$email_body" | mailx -s "$subject" -r "$from" -S replyto="$replyto" ${recipients[@]}
}

[...]
if [[ "--nomail" == "$1" ]]; then
    send_email=false
fi
[...]
if [[ true == "$send_email ]]; then
    handle_email
fi
0

Update

I went back and read your entire question and realized that I had originally answered a question slightly different than the one you asked, so here goes:

  1. Your structure is fine. Parse the command option at the beginning of the script.

  2. Yes, you can use a case statement, checking against $1 (see my original answer below), although if you are only planning a single option, you can just as easily perform a single test (see my original answer below). To use a case statement with multiple possible options, place it in a while loop, in the form while "$1" ; do case ... esac; shift; done. The shift command will replace $1 with the next word / option from your command line.

Original answer

There are several ways of dealing with this. If this is the only option, or if there are only a few, you may consider parsing it manually yourself. In shell-speak, all words after the shell command are called parameters, and each is assigned to a positional parameter, in the form $n with n starting at 1. Thus, you can perform a test to check whether [[ $1 == "--nomail" ]] and act accordingly. That's the simple straightforward way for only a single option.

For more complex shell scripts with many options, you may want to avail yourself of some form of getopts command to parse the options for you. The bash shell includes a version of getopts as a builtin, and you can learn more about it by typing man bash at the command line and then (if your default pager is set to less) /^ *getopt.

0

Other answers have already mentioned how to take a command-line option (i.e. checking "$1" directly or using getopts). My answer is on how to quickly modify your script to make use of a --nomail option without having to rewrite your script from scratch. In the long run, you should probably do that, if only to take advantage of the gradual improvements in your shell-scripting skill and knowledge.

The obvious way is to use a variable or a temporary file to store the output you want to either print or print & mail, and then choose what to do with it on each occasion.

The less obvious way is to replace the pipe to tee >(mailx ...) with a pipe to a function call that makes the decision to either tee its stdin to mailx or just cat it. With this method, there's no need to store the output, just pipe it and let the function deal with it - the rest of your code doesn't need to know or care (and, most importantly, doesn't need to be written to handle both cases).

Here's how...

First, add code like this somewhere near the start of your script:

# set the default, i.e. to send mail.
send_email=1

# Very basic option processing. Using getopts would be better.
[ "$1" == "--nomail" ] && send_email=''

myoutput() {
  if [ "$send_email" ] ; then
    # print stdin to stdout AND mail it
    tee <(mailx -s "$SUBJECT" -r "$FROM" -S replyto="$REPLYTO" "$TOLIST")
  else 
    # only print stdin to stdout. no mail.
    cat
  fi
}

Second, use the function defined here by replacing the | tee ... in your for loop with | myoutput.

(myoutput is a lame name for a function. rename it to cat_or_mail or whatever makes most sense to you)

Ideally, the function should take arguments rather than relying on global variables, and should be written something like this:

myoutput() {
  local send_email SUBJECT FROM REPYLTO TOLIST

  # more of the primitive option handling.  getopts can and should be used
  # inside a function too.    
  send_email="$1"
  SUBJECT="$2"
  FROM="$3"
  REPLYTO="$4"
  TOLIST="$5"

  # add code here to check if those variables contain valid values.
  # print warnings and/or abort and/or set appropriate defaults if they don't.

  if [ "$send_email" ] ; then
    # print stdin to stdout AND mail it
    tee <(mailx -s "$SUBJECT" -r "$FROM" -S replyto="$REPLYTO" "$TOLIST")
  else 
    # only print stdin to stdout. no mail.
    cat
  fi
}

and call it as ... | myoutput "$send_email" "$SUBJECT" "$FROM" "$REPLYTO" "$TOLIST"

This makes the function re-usable without having to set global variables beforehand - just call it with the values you need.

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.