Mail sent from variable is not aligned in shell script

Hi Folks :frowning:

Scenario,

Compared two values in two different files, if file1 value is greater than the value of file2 it trigger mail,

I tried this,

echo ${usep[@]};
echo ${usep1[@]};
for i in ${!usep[@]}; do
        if [ ${usep[$i]} -gt 80 ]; then
                if [ ${usep[$i]} -gt ${usep1[$i]} ]; then
                        tmp=$(echo "Running out of space \" ${usep[$i]}\" on $(hostname) as on $(date)");
                        alertlist="$tmp";
                        echo $alertlist;
                        echo "$alertlist" | mail -s "$ORACLE_SID ALERT:  Running out of space" $maillist;
                fi
        fi
done

The issue is,

when I try

alertlist+="$tmp";

Running out of space "/u01 (99%)"
Running out of space "/u01 (99%)" Running out of space "/u02 (96%)"
Running out of space "/u01 (99%)" Running out of space "/u02 (96%)" Running out of space "/u03 (90%)"

It append the values in variable, and sent the mail entirely in a single line.

Meanwhile when I try

alertlist="$tmp";

Running out of space "/u01 (99%)"
Running out of space "/u02 (96%)"
Running out of space "/u03 (90%)"

it looks good but it sent the mail for each mount point separately, which I receive individually 3 times.

I need to receive a mail which all mount point contain above 80 in single mail.
How can I do that?

I have no idea what your usep and usep1 arrays are supposed to contain, but it is obvious that the code you have shown us can't possibly produce the output you say you are getting. Both of those arrays need to contain numeric values for your code to work and there is nothing in your code that prints the filesystem names if both of those arrays contain numeric values???

It would seem that you want to

  1. take the mail command out of the loop,
  2. initialize alertlist to an empty string before you start your loop,
  3. use echo "$tmp" inside the loop,
  4. add the contents of the variable tmp as an additional line in the variable alertlist inside the loop, and
  5. after falling off the end of the loop, send mail if and only if ${#alertlist} is non-zero.

First of all thanks for response,

This is my entire code, you can find more details here.

export maillist=123@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
#df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > diskcheck.log;

#diskcheck is current output whereas disk_alert is previous runned output

if [ -s "$/monitor/log/disk_alert.log" ]; then
#Getting variables and compare with old
  usep=($(cat $/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $/monitor/diskcheck.log | awk '{ print $2 }' ));
  usep1=($(cat $/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
else
   cat "$/monitor/diskcheck.log" > "/monitor/log/disk_alert.log";
  usep=($(cat $/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $/monitor/diskcheck.log | awk '{ print $2 }' ));
  usep1=($(cat $/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
fi

echo ${usep[@]};
echo ${usep1[@]};
alertlist()
# index = i
for i in ${!usep[@]}; do
        if [ ${usep[$i]} -gt 80 ]; then
                if [ ${usep[$i]} -gt ${usep1[$i]} ]; then
                        #mail=awk '{ ${usep[$i]} print $0 }' $/monitor/diskcheck.log;
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"${partitions[$i]} (${usep[$i]}%)\" on $(hostname) as on $(date)")";
                        #tmp=$(printf "%s\n" "(${usep[$i]}%) percent used on ${partitions[$i]}");
                        #alertlist holds the values to send mail.
                        #alertlist+="$tmp";
                        echo "$tmp"
                        echo "$tmp" | mail -s "ALERT:  Running out of space" $maillist;
                        #echo "Running out of space \"${partitions[$i]} (${usep[$i]}%)\" on $(hostname) as on $(date)";
                fi
        fi
done
#echo "$alertlist" | mail -s "ALERT:  Running out of space" $maillist;

those arrays contain numeric values???
yes, arrays only contain numeric values.

1.take the mail command out of the loop,
Yes, I've tried that But it print only the last value of variable( alertlist )

2.initialize alertlist to an empty string before you start your loop,
Yes, tried it but won't work

3.use echo "$tmp" inside the loop,
Echo looks good while inside loop

4.add the contents of the variable tmp as an additional line in the variable alertlist inside the loop
I think it is similar as previous one, Right?

Have a look at the summary.

Thanks

Please show us the code where you tried what I suggested in post #2 in this thread. Steps 1, 2, 4, and 5 of what I suggested do not appear in the code you showed us in post #3.

The code that you did show us in post #3 defines a function named alertlist whose body depends on what shell you're using (probably either the nested for loop in your code, a syntax error, or a comment) and which is never invoked in your script.

What operating system and shell are you using?

Do you really have a directory named $ in the directory in which you run this script? Using a name like that is certainly possible, but using it can frequently be accident prone. (Or does $/ have some special meaning in the shell you're using?)

Hello Don,

sorry for late response I was out of my location, Here I've attached code as you suggested and updated,

1.) Please show us the code where you tried what I suggested in post #2 in this thread. Steps 1, 2, 4, and 5 of what I suggested do not appear in the code you showed us in post #3.

export maillist=abc@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > $HOME/monitor/log/diskcheck.log;

#diskcheck is current output whereas disk_alert is previous runned output

if [ -s "$HOME/monitor/log/disk_alert.log" ]; then
#Getting variables and compare with old
  usep=($(cat $HOME/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $HOME/monitor/diskcheck.log | awk '{ print $2 }' ));
  usep1=($(cat $HOME/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
else
   cat "$HOME/monitor/diskcheck.log" > "$HOME/monitor/log/disk_alert.log";
  usep=($(cat $HOME/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $HOME/monitor/diskcheck. | awk '{ print $2 }' ));
  usep1=($(cat $HOME/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
fi

echo ${usep[@]};
echo ${usep1[@]};
alertlist=''
# index = i
for i in ${!usep[@]}; do
        if [ ${usep[$i]} -gt 80 ]; then
                if [ ${usep[$i]} -gt ${usep1[$i]} ]; then
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"${partitions[$i]} (${usep[$i]}%)\" on $(hostname) as on $(date)")";
                        echo "$tmp"
                        #tmp=$(printf "%s\n" "(${usep[$i]}%) percent used on ${partitions[$i]}");
                        alertlist+="$tmp";

                fi
        fi
done
if [ ! -z "${alertlist}" ]; then
        echo "${alertlist}" | mail -s "ALERT:  Running out of space" $maillist;
fi

a.) use echo "$tmp" inside the loop! the result is

19 1 39 3 90 83 1 14 96
19 1 39 3 40 83 1 14 66
Running out of space "/u05 (90%)" on localhost.local as on Mon May 15
Running out of space "/u02 (96%)" on localhost.local as on Mon May 15

b.) The code that you did show us in post #3 defines a function named alertlist whose body depends on what shell you're using!
bash_version 4.1.2(1)-release

and finally the output of above script is

Running out of space "/d01 (90%)" on localhost.local as on Mon May 15 Running out of space "/patches (96%)" onlocalhost.local as on Mon May 15 

all it shows in a single line, but echo of "$tmp" variable is stored clear. But I need mail which contains information as it's stored in "$tmp" with proper alignment, Because now 2 of the mount points only passed our condition if it was more than 10 it's very complex to understand.

2.)What operating system and shell are you using?
RHEL 6.8
bash_version 4.1.2(1)-release

3.)Do you really have a directory named $ in the directory in which you run this script?
it refers the directory where the files are located, it's actually $HOME/monitor/
$HOME is home directory of username in my case username is vijay ,
So

vijay/monitor

4.)Or does $/ have some special meaning in the shell you're using?
No, it redirect to the directory called $HOME/monitor . in post #1 I conclude that information.

Hope this will help.
[mod]Please use CODE tags as required by forum rules!

Try changing your script to:

export maillist=abc@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > $HOME/monitor/log/diskcheck.log;

#diskcheck is current output whereas disk_alert is previous runned output

if [ -s "$HOME/monitor/log/disk_alert.log" ]; then
#Getting variables and compare with old
  usep=($(cat $HOME/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $HOME/monitor/diskcheck.log | awk '{ print $2 }' ));
  usep1=($(cat $HOME/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
else
   cat "$HOME/monitor/diskcheck.log" > "$HOME/monitor/log/disk_alert.log";
  usep=($(cat $HOME/monitor/diskcheck.log | awk '{ print $1 }' | cut -d'%' -f1));
  partitions=($(cat $HOME/monitor/diskcheck. | awk '{ print $2 }' ));
  usep1=($(cat $HOME/monitor/log/disk_alert.log | awk '{ print $1 }' | cut -d'%' -f1));
fi

echo ${usep[@]};
echo ${usep1[@]};
unset alertlist
# index = i
for i in ${!usep[@]}; do
        if [ ${usep[$i]} -gt 80 ]; then
                if [ ${usep[$i]} -gt ${usep1[$i]} ]; then
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"${partitions[$i]} (${usep[$i]}%)\" on $(hostname) as on $(date)")";
                        echo "$tmp"
                        #tmp=$(printf "%s\n" "(${usep[$i]}%) percent used on ${partitions[$i]}");
                        alertlist=("${alertlist[@]" "$tmp")
                fi
        fi
done
if [ ${#alertlist} -gt 0 ]; then
        printf '%s\n' "${alertlist[@]}" | mail -s "ALERT:  Running out of space" $maillist;
        echo "Following text sent as body of mail to $maillist:"
        printf '%s\n' "${alertlist[@]}"
fi

and tell us what happens.

Note, however, that if a mounted filesystem is ever unmounted or a new filesystem is mounted between runs of your script, there is no guarantee that the order of the percentage values stored in the arrays usep and usep1 are in the same order. And note that if $HOME/monitor/log/disk_alert.log is not the name of an existing file whose size is larger than 0, the assignment to the partitions array will fail since you're reading the file diskcheck. instead of diskcheck.log :

  partitions=($(cat $HOME/monitor/diskcheck. | awk '{ print $2 }' ));

and all of your assignments like this are wasting CPU and memory by using cat when it is not needed. Try:

  partitions=($(awk '{ print $2 }' $HOME/monitor/diskcheck.log));

instead.

Hello Don,

As you suggest I tried the new script,

a.)all of your assignments like this are wasting CPU and memory by using cat when it is not needed. Try:
Yes

export maillist=abc@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > $HOME/monitor/diskcheck.log;

if [ -s "$HOME/monitor/log/disk_alert.log" ]; then
#Getting variables and compare with old
  usep=($(awk '{ print $1 }' $HOME/monitor/diskcheck.log | cut -d'%' -f1));
  partitions=($(awk '{ print $2 }' $HOME/monitor/diskcheck.log | cut -d'%' -f1));
  usep1=($(awk '{ print $1 }' $HOME/monitor/log/disk_alert.log | cut -d'%' -f1));
else
   cat "$HOME/monitor/diskcheck.log" > "$HOME/monitor/log/disk_alert.log";
  usep=($(awk '{ print $1 }' $HOME/monitor/diskcheck.log | cut -d'%' -f1));
  partitions=($(awk '{ print $2 }' $HOME/monitor/diskcheck.log | cut -d'%' -f1));
  usep1=($(awk '{ print $1 }' $HOME/monitor/log/disk_alert.log | cut -d'%' -f1));
fi

echo ${usep[@]};
echo ${usep1[@]};
unset alertlist
# index = i
for i in ${!usep[@]}; do
        if [ ${usep[$i]} -gt 80 ]; then
                if [ ${usep[$i]} -gt ${usep1[$i]} ]; then
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"${partitions[$i]} (${usep[$i]}%)\" on $(hostname) as on $(date)")";
                        echo "$tmp"
                        #tmp=$(printf "%s\n" "(${usep[$i]}%) percent used on ${partitions[$i]}");
                        alertlist=("${alertlist[@]}" "$tmp")
                fi
        fi
done

if [ ${#alertlist} -gt 0 ]; then
        printf '%s\n' "${alertlist[@]}" | mail -s "ALERT:  Running out of space" $maillist;
        echo "Following text sent as body of mail to $maillist:"
        printf '%s\n' "${alertlist[@]}"
fi                     #alertlist holds the values to send mail.

While I run the script it seems everything okay as usual the output of run script is,

[vijay@localhost monitor]$ sh disk_alert.sh
19 1 39 3 40 93 1 14 96
19 1 39 3 40 83 1 14 66
Running out of space "/u05 (93%)" on localhost.local as on Tue May 16 
Running out of space "/u02 (96%)" on localhost.local as on Tue May 16 
Following text sent as body of mail to abc@gmail.com:
Running out of space "/u05 (93%)" on localhost.local as on Tue May 16 
Running out of space "/u02 (96%)" on localhost.local as on Tue May 16 

When I receive the mail it's remains with the old format

Running out of space "/u05 (93%)" on localhost.local as on Tue May 16 Running out of space "/u02 (96%)" on localhost.local as on Tue May 16 

1.)
Note, however, that if a mounted filesystem is ever unmounted or a new filesystem is mounted between runs of your script, there is no guarantee that the order of the percentage values stored in the arrays usep and usep1 are in the same order!

Is their any solution for this?

Yes. But we need more information.

With this latest version of your script we know that the text being sent is correctly formatted and that your recipient's mail reader is reformatting the text in the message before displaying it.

What application is being used by abc@gmail.com to read mail?

If the mail reader being used is expecting HTML input, the thread Sendmail ignoring line endings
might provide the information we need to work around your problem by changing the line:

        printf '%s\n' "${alertlist[@]}" | mail -s "ALERT:  Running out of space" $maillist;

to something like:

        { echo '<HTML><BODY>'
          printf '%s<br />\n' "${alertlist[@]}"
          echo '</HTML></BODY>'
        } | mail -s "ALERT:  Running out of space" $maillist;

And for the part of your previous post asking about filesystem mount order...

Use associative arrays instead of indexed arrays for the percentages (using the filesystem mount point as the subscript). I don't have access to a 4.x version of bash so, this is totally untested, but it should come close to what you need (including the previously suggested HTML mail body changes) and run faster (since all of the invocations of awk and cut are gone):

export maillist=abc@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > $HOME/monitor/diskcheck.log;

unset usep usep1
declare -A usep usep1

if [ -s "$HOME/monitor/log/disk_alert.log" ]; then
  #Getting variables and compare with old
  while read -r pct fs
  do	pct=${pct%\%}
	usep["$fs"]=$pct
	printf '%s(%s) ' "$fs" "$pct"
  done < "$HOME/monitor/diskcheck.log"
  echo
  while read -r pct fs
  do	pct=${pct%\%}
	usep1["$fs"]=$pct
	printf '%s(%s) ' "$fs" "$pct"
  done < "$HOME/monitor/log/disk_alert.log"
  echo
else
  cp "$HOME/monitor/diskcheck.log" "$HOME/monitor/log/disk_alert.log"
  # Note that if disckcheck.log and disk_alert.log are identical, usep[$i] can
  #   never be greater than usep1[$i], so we might as well quit now.
  exit
fi

unset alertlist
for fs in ${!usep[@]}; do
        if [ ${usep["$fs"]} -gt 80 ]; then
                if [ ${usep["$fs"]} -gt ${usep1["$fs"]} ]; then
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"$i (${usep["$fs"]}%%)\" on $(hostname) as on $(date)")";
                        echo "$tmp"
                        #tmp=$(printf "%s\n" "(${usep["$fs"]}%%) percent used on $i");
                        alertlist=("${alertlist[@]}" "$tmp")
                fi
        fi
done

if [ ${#alertlist} -gt 0 ]; then
	{ echo '<HTML><BODY>'
	  printf '%s<br />\n' "${alertlist[@]}"
	  echo '</HTML></BODY>'
	} | mail -s "ALERT:  Running out of space" $maillist
	echo "Following text sent as body of mail to $maillist:"
	printf '%s\n' "${alertlist[@]}"
fi
Yes. But we need more information.

With this latest version of your script we know that the text being sent is correctly formatted and that your recipient's mail reader is reformatting the text in the message before displaying it.

What application is being used by abc@gmail.com to read mail?

It uses MS Outlook,

If the mail reader being used is expecting HTML input, the thread Sendmail ignoring line endings
might provide the information we need to work around your problem by changing the line:

Yeah, I've gone through the thread Sendmail ignoring line endings and found that

The only one NOT adhering to this convention is perhaps your mail reader program (more correctly: mail user agent), which is probably Microsoft Outlook or similar crap. Because against all convention, laid down in RFCs, M$$ once decreed that email is not any longer plain ASCII text by default with MIME-parts in HTML being allowed, but in fact email is HTML from the start (to be precise: not exactly HTML, but what M$$ took as being being HTML, which wasn't quite up to the standard either).

then I tried with your script,

Code:
        printf '%s\n' "${alertlist[@]}" | mail -s "ALERT:  Running out of space" $maillist;

to something like:

Code:
        { echo '<HTML><BODY>'
          printf '%s<br />\n' "${alertlist[@]}"
          echo '</HTML></BODY>'
        } | mail -s "ALERT:  Running out of space" $maillist;

This is my updated script part

if [ ${#alertlist} -gt 0 ]; then
     #printf '%s\n' "${alertlist[@]}" | mail -s "ALERT:  Running out of space" $maillist;
        {
          printf '%s' "${alertlist[@]}\n"
        } | mail -s "ALERT:  Running out of space" $maillist;
      echo "Following text sent as body of mail to $maillist:"
      printf '%s\n' "${alertlist[@]}"
fi                     #alertlist holds the values to send mail.

I removed

echo '<HTML><BODY>'

Because it prints in mail,

the output is

 <HTML><BODY>
Running out of space "/u02 (94%)" on localhost.local as on Wed May 17  <br />Running out of space "/u05 (99%)" on localhost.local as on Wed May 17<br /></HTML></BODY>

am using MS Outlook 2013, what it does seems it prints all the line along <HTML>

---------- Post updated at 11:00 PM ---------- Previous update was at 10:58 PM ----------

export maillist=abc@gmail.com;
#df -PH /d04 /d05 /u01 /export | grep -vE '^Filesystem|none|cdrom'|awk '{ print $5 " " $6 }' | while read output;
df -PH | grep -vE '^Filesystem|none|cdrom|swdepot'|awk '{ print $5 " " $6 }' > $HOME/monitor/diskcheck.log;

unset usep usep1
declare -A usep usep1

if [ -s "$HOME/monitor/log/disk_alert.log" ]; then
  #Getting variables and compare with old
  while read -r pct fs
  do	pct=${pct%\%}
	usep["$fs"]=$pct
	printf '%s(%s) ' "$fs" "$pct"
  done < "$HOME/monitor/diskcheck.log"
  echo
  while read -r pct fs
  do	pct=${pct%\%}
	usep1["$fs"]=$pct
	printf '%s(%s) ' "$fs" "$pct"
  done < "$HOME/monitor/log/disk_alert.log"
  echo
else
  cp "$HOME/monitor/diskcheck.log" "$HOME/monitor/log/disk_alert.log"
  # Note that if disckcheck.log and disk_alert.log are identical, usep[$i] can
  #   never be greater than usep1[$i], so we might as well quit now.
  exit
fi

unset alertlist
for fs in ${!usep[@]}; do
        if [ ${usep["$fs"]} -gt 80 ]; then
                if [ ${usep["$fs"]} -gt ${usep1["$fs"]} ]; then
                        # Temp to store current percentage diff and partition to store in a variable.
                        tmp="$(echo -e "Running out of space \"$i (${usep["$fs"]}%%)\" on $(hostname) as on $(date)")";
                        echo "$tmp"
                        #tmp=$(printf "%s\n" "(${usep["$fs"]}%%) percent used on $i");
                        alertlist=("${alertlist[@]}" "$tmp")
                fi
        fi
done

if [ ${#alertlist} -gt 0 ]; then
	{ echo '<HTML><BODY>'
	  printf '%s<br />\n' "${alertlist[@]}"
	  echo '</HTML></BODY>'
	} | mail -s "ALERT:  Running out of space" $maillist
	echo "Following text sent as body of mail to $maillist:"
	printf '%s\n' "${alertlist[@]}"
fi

Thank you so much for sharing this kinda stuff, I would go implement this thing once the mailing part is succeed.