To configure script to send email only when diff command gives a difference

9,978

The problem is the command you're using to do the diff. Every time that command runs the results of it, whether there's a difference or not, will trigger the mail command.

Example

Here we're going to simulate the 2 files using 2 echo commands like so:

$ diff <(echo 1) <(echo 2)
1c1
< 1
---
> 2

That seems OK, but what happens when the echo commands are the same thing?

$ diff <(echo 1) <(echo 1)
$ 

OK, so that doesn't seem to be an issue, or is it? What if we pipe the output from the diff command to say another echo command:

$ diff <(echo 1) <(echo 1) | echo "hi"
hi

See even empty output from diff piped to another command will trigger that command to run.

So then what's the right way?

What you really want to do is one of 2 things. Either check the status of the diff command, or use a logical operator such and && (and) or the operator || (or operator).

Status

Most commands when executed return a status. Usually a zero or a one to denote that they ran successfully or failed. You can see the status from diff by analyzing the status variable $? in Bash.

diff returns a 0 if the files match

$ diff <(echo 1) <(echo 1)
$ echo $?
0

diff returns a 1 if the files don't match

$ diff <(echo 1) <(echo 2)
1c1
< 1
---
> 2

$ echo $?
1

So by itself it seems useful, we can tell whether a command ran successfully or not. However we don't have a way to act on this. So let's look at the logical operators.

Logical operator

The other method is to change 2 commands so that if the first one is successful, then the second one will run. Conversely you can use the alternative operator to run a second command, if the first failed.

Example

different pragma

$ diff <(echo 1) <(echo 2) || echo "they're different"
1c1
< 1
---
> 2
they're different

same pragma

$ diff <(echo 1) <(echo 1) && echo "they're the same"
they're the same

NOTE: Some caution should be used if you use this method. There is one issue with type of structure, because the above are true logical operators in the same way that a if .. then .. else is. You can read about these on the bashpitfalls website.

So this seems like a valid approach. But maybe there's another way. Keep reading.

If then else

A third method would be to enlist a if/then block to run the mail program. if/then commands are related to the logical operators I mentioned a minute ago, they're called conditionals.

NOTE: Also diff really isn't the best tool for use in if/then statements. It's better to use the cmp command which can return just a status and nothing more. So let's upgrade that now too.

Example

same

$ cmp -s <(echo 1) <(echo 1)
$ echo $?
0

different

$ cmp -s <(echo 2) <(echo 1)
$ echo $?
1

Extending this into an if/then block would look something like this:

$ if cmp -s <(echo 2) <(echo 1);then echo "same"; else echo "different";fi
different

$ if cmp -s <(echo 1) <(echo 1);then echo "same"; else echo "different";fi
same

Your problem

You could then do something like this for your original problem:

if cmp -s switchshow_reference switchshow_results; then
  diff switchshow_reference switchshow_results \
     | mail -s device_PORT_ERROR email_recipient
fi

UPDATE #1

In looking at your updated script you need to make the following modifications:

ssh test@ip_of_device switchshow > switchshow_results

variable=$(diff switchshow_reference switchshow_results)

if [[ $variable -eq 0 ]]    
then    
  echo $"nothing"
else    
  echo -n "$variable" | mail -s switch_HARDWARE_CHECK  recipeint_email_address

fi

The issues you were running into with the output showing up as a single line were caused by your use of echo without telling it to expand special characters, such as, \n. For example:

$ echo "oneline\ntwoline"
oneline\ntwoline

$ echo -e "oneline\ntwoline"
oneline
twoline

One other modification I made was in how you executed the diff. This didn't impact your functionality in a bad way, but the backticks (\..`) has been deprecated in favor of this notation,$(...)`. These have the added benefit of being able to nest commands inside of other commands such as:

$ myvar=$(echo "good + $(echo bye)")
$ echo $myvar
good + bye
Share:
9,978

Related videos on Youtube

user46359
Author by

user46359

Updated on September 18, 2022

Comments

  • user46359
    user46359 over 1 year

    I'm new to the world of scripting and and currently using a script to perform a health check on the san devices that I manage.

    The script is simple and redirects output of commands to a current file from remote device to my local host. I then use a diff command to compare output of current file and reference file and get a email alert of the difference.

    However the problem that I'm currently facing is that every time I run the script, I get a email after the script is executed, that is even if there is no difference in the comparison, I get a blank email from the script execution.

    As this is related to performing a automated health check, getting a blank email everyday will be a nuisance. Is there any way of tweaking the diff or add some more code to make sure that when there is no difference showing from the diff, I should not get a email and only get a email if there is a difference and showing the difference.

    current script

    ### saves output of command switchshow in a file on local host
    ssh user@ip_of_switch switchshow > switchshow_results
    
    ### Compares current output to reference file and mails the difference
    diff switchshow_reference switchshow_results \
        | mail -s device_PORT_ERROR email_recipient
    

    Please let me know if any other information is required.


    Thanks for the help and details explanation Slm :)

    I have so far got the email part working (I get an email only if there is a difference from diff command). However the email that I now get has its test concatenated into one long string, which is bad for viewing and not easy to understand for the people who will be getting it.

    My current script is pasted below:

    ssh test@ip_of_device switchshow > switchshow_results
    
    variable=`diff switchshow_reference switchshow_results`
    
    if [[ $variable -eq 0 ]]    
    then    
      echo $"nothing"    
    else    
      echo $variable | mail -s switch_HARDWARE_CHECK  recipeint_email_address    
    fi
    

    The output of the above script is blank and I don't get any email when the diff command does not find any difference. However when the diff commands finds a difference, I get a email as pasted below:

    16c16 < 0 0 010000 id N4 Online FC F-Port 1 N Port + 1 NPIV public --- > 0 0 010000 id N4 No_Light FC F-Port 1 N Port + 1 NPIV public 26c26 < 10 10 010a00 id N4 Online FC F-Port 1 N Port + 1 NPIV public --- > 10 10 010a00 id N4 No_Light FC F-Port 1 N Port + 1 NPIV public 29c29 < 13 13 010d00 id N4 Online FC F-Port 50:06:01:67:3b:20:23:0a --- > 13 13 010d00 id N4 No_Light FC F-Port 50:06:01:67:3b:20:23:0a
    

    This should ideally look like this in the email..

    16c16 < 0 0 010000 id N4 Online FC F-Port 1 N Port + 1 NPIV public --- > 0 0 010000 id N4 No_Light FC F-Port 1 N Port + 1 NPIV public 
    
    26c26 < 10 10 010a00 id N4 Online FC F-Port 1 N Port + 1 NPIV public --- > 10 10 010a00 id N4 No_Light FC F-Port 1 N Port + 1 NPIV public
    
    29c29 < 13 13 010d00 id N4 Online FC F-Port 50:06:01:67:3b:20:23:0a --- > 13 13 010d00 id N4 No_Light FC F-Port 50:06:01:67:3b:20:23:0a
    

    Output should display one line at a time in the email. Any suggestions?

    • Marki
      Marki over 9 years
      I'd suggest you get acquainted with Nagios, Icinga, and the like ;-)
  • user46359
    user46359 over 10 years
    Thanks for the Help SLM :) I have made a lot of progress , but I am currently stuck at one last thing. I have edited the above question with my new info. Can you please look into it and help me out? Your help will be much appreciated.