My requirement is to send an email if I find a string in a log file; however, I should be sending it only once. The shell script I have written is pasted below; however, it is sending repeated emails via cron job even when the condition is not matching.
#!/bin/bash
filexists=""
lbdown=""`enter code here`
if [ -f "/var/run/.mailsenttoremedy" ];
then
filexists=true
else
filexists=false
echo filexists is $filexists
fi
if tail -1000 /usr/ibm/tivoli/common/CTGIM/logs/trace.log | grep "Root exception is java.net.NoRouteToHostException: No route to host"
then
echo error found
lbdown=true
echo lbdown status after if in tail is $lbdown
else
lbdown=false
echo lbdown status after else in tail is $lbdown
fi
if filexists=false && lbdown=true
then
{
mailx -S intrelay.sysco.com -r xxx#yyy.com -s "**DEV ALERT**Load Balancer Connection not Available" -v xxx#yyy.com < /dev/null
date > /var/run/.mailsenttoremedy
}
fi
if filexists=true && lbdown=true
then
{
echo MAIL ALREADY SENT
}
fi
if lbdown=false
then
rm -f /var/run/.mailsenttoremedy
fi
echo lbdown is $lbdown and filexists is $filexists
echo outputs are:
filexists is false
Root exception is java.net.NoRouteToHostException: No route to host
error found
lbdown status after if in tail is true
Null message body; hope that's ok
Mail Delivery Status Report will be mailed to <xxx#yyy.com>.
MAIL ALREADY SENT
lbdown is false and filexists is true
You could try a normal declaration for the if requests...
Bash format:
if [ $(tail -1000 /usr/ibm/tivoli/common/CTGIM/logs/trace.log | grep "Root exception is java.net.NoRouteToHostException: No route to host") != "" ];
then
if [ "$filexists" = "false" ] && [ "$lbdown" = "true" ];
then
if [ "$lbdown" = "false" ];
then
Test commands via if should be surrounded by [ and ], at least the ones with multiple conditions. Here is a guide for if and some sample codes if you are interested.
In addition, variables need a $ in front of them at least. Normally they are surrounded by { and } too.
PS. You could use a better format for the post so people won't downvote you.
For others, the working code is:
#!/bin/bash
filexists=""
lbdown=""
if [ -f "/var/run/.mailsenttoremedy" ];
then
filexists=true
else
filexists=false
echo filexists is $filexists
fi
if tail -1000 /usr/ibm/tivoli/common/CTGIM/logs/trace.log | grep "nn"
then
echo error found
lbdown=true
echo lbdown status after if in tail is $lbdown
else
lbdown=false
echo lbdown status after else in tail is $lbdown
fi
if [[ "$filexists" = "false" && "$lbdown" = "true" ]];
then
mailx -S intrelay.sysco.com -r xxx#yyy.com -s "**DEV ALERT**Load Balancer Connection not Available" -v xxx#yyy.com < /dev/null
date > /var/run/.mailsenttoremedy
fi
if [[ "$filexists" = "true" && "$lbdown" = "true" ]];
then
echo MAIL ALREADY SENT
fi
if [ "$lbdown" = "false" ];
then
rm -f /var/run/.mailsenttoremedy
echo removing file
fi
echo lbdown is $lbdown and filexists is $filexists
Related
Context
I finally fixed my issue to have stdout & stderr to screen and a file plus having a separate file for the errors. See: Output stdout and stderr to file and screen and stderr to file in a limited environment
Issue
The problem is the ordering of lines.
Some times it is OK:
FIRST
ERROR
LAST
ERROR2
LAST2
Some times the errors are at the end:
FIRST
LAST
LAST2
ERROR
ERROR2
I can't figure out why (except maybe a semaphore underneath that... but... not sure. And if it is the case, there is no solution exception adding line numbers to each echo).
Part of the code where the problem occurs
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
Full testable code
m=0
declare -a mainArgs
if [ ! "$#" = "0" ]; then
for arg in "$#"; do
mainArgs[$m]=$arg
m=$(($m + 1))
done
fi
function containsElement()
# $1 string to find
# $2 array to search in
# return 0 if there is a match, otherwise 1
{
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
function hasMainArg()
# $1 string to find
# return 0 if there is a match, otherwise 1
{
local match="$1"
containsElement "$1" "${mainArgs[#]}"
return $?
}
function activateLogs()
# $1 = logOutput: What is the output for logs: SCREEN, DISK, BOTH. Default is DISK. Optional parameter.
{
local logOutput=$1
if [ "$logOutput" != "SCREEN" ] && [ "$logOutput" != "BOTH" ]; then
logOutput="DISK"
fi
if [ "$logOutput" = "SCREEN" ]; then
echo "Logs will only be output to screen"
return
fi
hasMainArg "--force-log"
local forceLog=$?
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
echo "Logs are configured"
elif [ "$forceLog" = "1" ] && ([ ! -t 1 ] || [ ! -t 2 ]); then
# Use external file descriptor if they are set except if having "--force-log"
echo "Logs are configured externally"
else
echo "Relaunching with logs files"
local logPath="logs"
if [ ! -d $logPath ]; then mkdir $logPath; fi
local logFileName=$(basename "$0")"."$(date +%Y-%m-%d.%k-%M-%S)
exec 4<> "$logPath/$logFileName.log" # File descriptor created only to get the underlying file in any output option
if [ "$logOutput" = "DISK" ]; then
# FROM: https://stackoverflow.com/a/45426547/214898
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
else
# FROM: https://stackoverflow.com/a/70790574/214898
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
fi
exit
fi
}
#activateLogs "DISK"
#activateLogs "SCREEN"
activateLogs "BOTH"
echo "FIRST"
echo "ERROR" >&2
echo "LAST"
echo "ERROR2" >&2
echo "LAST2"
Stdout 1 and errout 2 pass by different file descriptors. If is very busy then very close calls like these can get mixed up.
You could use sleep 1 between your calls which would give the system time to process each call in order, but would slow down your script.
You can sleep less than a second : for example sleep 0.5.
I want to check for file in directory if there then push it to ssh server checing server connection if file not there then try 3 times with each 1min interval and in between if it comes ( on 2nd attend for example) then try again to connect ssh and push. else check for 3 attempts and exit
Please check my below code it is halting after 1st attempt ( during 2nd attempt I am making file available)
#!/bin/sh
echo "OK, start pushing the Userdetails to COUPA now..."
cd /usr/App/ss/outbound/usrdtl/
n=0
until [ $n -ge 3 ] || [ ! -f /usr/App/ss/outbound/usrdtl/USERS_APPROVERS_*.csv ]
do
if [ -f /usr/App/ss/outbound/usrdtl/USERS_APPROVERS_*.csv ] ;
then
pushFiles()
else
n=$[$n+1]
sleep 60
echo " trying " $n "times "
fi
done
pushFiles()
{
echo "File present Now try SSH connection"
while [ $? -eq 0 ];
do
echo $(date);
scpg3 -v /usr/App/ss/outbound/usrdtl/USERS_APPROVERS_*.csv <sshHost>:/Incoming/Users/
if [ $? -eq 0 ]; then
echo "Successfull"
echo $(date);
echo "Successfull" >> /usr/App/ss/UserApproverDetails.log
exit 1;
else
echo $(date);
echo "Failed" >> /usr/App/ss/UserApproverDetails.log
echo "trying again to push file.."
scpg3 -v /usr/App/sg/outbound/usrdtl/USERS_APPROVERS_*.csv <ssh Host>:/Incoming/Users/
echo $(date);
exit 1;
fi
done
}
I've tried to simplify this code for you. I hope it helps:
#!/bin/bash
outdir="/usr/App/ss/outbound/usrdtl"
logfile="/usr/App/ss/UserApproverDetails.log"
file_prefix="USERS_APPROVERS_"
function push_files() {
echo "File present now try SSH connection"
local attempts=1
local retries=2
date
while [[ ${attempts} -lt ${retries} ]]; do
if scp ${outdir}/${file_prefix}*.csv <sshHost>:/Incoming/Users/ ; then
echo "Successful" | tee -a ${logfile}
date
exit 0
else
echo "Failed" >> ${logfile}
fi
attempts=$((attempts+1))
do
echo "scp failed twice" | tee -a ${logfile}
exit 2
}
echo "OK, start pushing the Userdetails to COUPA now..."
cd ${outdir}
attempts=1
retries=3
while [[ ${attempts} -lt ${retries} ]]; do
echo "looking for files...attempt ${attempts}"
if test -n "$(shopt -s nullglob; echo ${outdir}/${file_prefix}*.csv)"; then
push_files()
fi
attempts=$((attempts+1))
sleep 60
done
echo "Files were never found" | tee -a ${logfile}
exit 1
Look at this code and tell me how it's not doing what you're trying to do. The most complicated part here is the nullglob stuff, which is a handy trick to see if any file in a glob matches
Also, I generally used bashisms.
I wrote a shell script for detecting whether a package is installed or not. My script should write its name and status if it's installed. I can't figure out any problem with my code but when I run it, it doesn't execute the commands under if [ $? == 0 ] condition.
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
But the most weird thing to me is that it works if I add an echo after if statement. Looks like that:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
echo hi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
So if I add an echo -n to right position in my code it will work as I want. But I just want to know what is wrong with first one?
I think in general you could be more deliberate about your return code handling. You are making assumptions about what $? is referring to that may not be valid depending on your program flow, and regardless, make the program harder to read and understand.
#!/bin/bash
dpkg -s $# &> /dev/null
installed=$?
if [ $installed -eq 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi
$? is the return status of the last executed command. 0 is successful, 1 or anything else is an error. Note:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
[ #? == 0 ] # false # returns 1 (error)
When you put echo, it works:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
echo hi # returns 0 (OK)
# $? equals 0 now
[ #? == 0 ] # true # returns 0 (OK)
You could save $? to a variable, but you don't really need the if inside the else since you already checked if #? == 1 so just put your code inside else:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
If you are worried of other possible return statuses of $? (greater than one). You could rewrite your script to
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi
i wrote alert log script and some how this is not working and not throwing any error when i execute the script
i am suspecting sed part is not working properly. could you please advice where i am doing wrong?
here is the piece of code
#!/bin/sh
## Heading #########################################################################################
#---------------------------------------------------------------------------------------#
# script usage #
#---------------------------------------------------------------------------------------#
_usage() {
echo "Usage: $0 ORACLE_SID "
} # _usage
ORACLE_SID="$1"
setenv ()
{
eval "$1=$2"
export "$1"
} # setenv
unsetenv ()
{
while [ $# -gt 0 ]
do
unset "$1"
shift
done
} # unsetenv
if [ $# -ne 1 ]; then
_usage
exit 1
fi
Env=/u01/app/oracle/config
HN=`uname -n`
ERROR_FILE=/tmp/${ORACLE_SID}_error.log
HN=`hostname`
DBA_MAIL="oracle.mail#company"
DBA_PAGE=""
#+--------------------------------------------------------------------------------------+
#| get oracle environment variables from our common env dir |
#+--------------------------------------------------------------------------------------+
if [ -r $Env/${ORACLE_SID}.env ]
then
. $Env/${ORACLE_SID}.env
else
ORACLE_SID=""
fi
#+--------------------------------------------------------------------------------------+
#| just checking for Oracle Env variables for connecting database |
#+--------------------------------------------------------------------------------------+
if [ "$ORACLE_SID" = "" ]
then
echo "ORACLE_SID is invalid"
exit 1
fi
if [ "$ORACLE_HOME" = "" ]
then
echo "The environment variable ORACLE_HOME must be set"
exit 1
fi
if [ "$ORACLE_BASE" = "" ]
then
echo "The environment variable ORACLE_BASE must be set"
exit 1
fi
_AlertLogLoc ()
{
ALERTLOG=`$ORACLE_HOME/bin/sqlplus -s "/as sysdba" << EOF
set head off pause off term on feed off timing off
select value from v\\$parameter where name like 'background_dump_dest';
exit;
EOF`
}
_AlertLogLoc
echo $ALERTLOG
export ALERTLOG
if [ -f $ALERTLOG/alert_${ORACLE_SID}.log ]; then
echo "Found Database Alert log"
else
echo "Alert log not found .. exit from script"
fi
if [ -f $ALERTLOG/alert_${ORACLE_SID}.skip ]; then
echo " ORACLE_SID skip error file found"
SKIP_ERR=`cat $ALERTLOG/alert_$ORACLE_SID.skip|xargs|sed -e 's/ /|/g'`
echo $SKIP_ERR
else
echo "No errors will be excluded"
fi
REC_CUR_ALSIZE=/oraworkspace/OSE/logs/alert_${ORACLE_SID}.size # file to record current alert log lines
#---------------------------------------------------------------------------------------------------------#
# let Capture ORA- error from the alert log #
#---------------------------------------------------------------------------------------------------------#
if [ -f $REC_CUR_ALSIZE ]; then
ALSIZE=`cat $REC_CUR_ALSIZE|sed -e 's/^[ \t]*//'`
ALSIZE=`expr $ALSIZE + 1`
else
ALSIZE=0
fi
if [ $ALSIZE -eq 0 ]; then
echo "PROBABLY RUNNUNG THE SCRIPT FIRST TIME"
sed -n $ALSIZE',$p' $ALERTLOG/alert_${ORACLE_SID}.log |egrep -v "$SKIP_ERR"|grep -i 'ORA-' > /tmp/${ORACLE_SID}_error.log
#`wc -l $ALERTLOG/alert_${ORACLE_SID}.log > $REC_CUR_ALSIZE
cat $ALERTLOG/alert_${ORACLE_SID}.log|wc -l > /oraworkspace/OSE/logs/alert_${ORACLE_SID}.size
#ALSIZE=`cat $ALERTLOG/alert_${ORACLE_SID}.log |wc -l`
else
sed -n ${ALSIZE}',$p' $ALERTLOG/alert_${ORACLE_SID}.log |egrep -v "$SKIP_ERR"|grep -i 'ORA-' > /tmp/${ORACLE_SID}_error.log
#wc -l $ALERTLOG/alert_${ORACLE_SID}.log >> $REC_CUR_ALSIZE
cat $ALERTLOG/alert_${ORACLE_SID}.log |wc -l > /oraworkspace/OSE/logs/alert_${ORACLE_SID}.size
fi
#---------------------------------------------------------------------------------------------------------#
# Notify if any errors are found #
#---------------------------------------------------------------------------------------------------------#
ERR_CNT=`cat /tmp/${ORACLE_SID}_error.log |wc -l`
if [ $ERR_CNT -ne 0 ]; then
echo "Errors found in the alert log. send email notification"
mailx -s "${HN}:${ORACLE_SID} ORA error Found in the alert log" ${DBA_MAIL} < $ERROR_FILE
#mailx -s "${HN}:${ORACLE_SID} ORA error Found in the alert log" ${DBA_MAIL} < $ERROR_FILE
else
echo " No errors found in the alert log"
fi
If $ALERTLOG/alert_$ORACLE_SID.skip doesn't exist or is empty (around line 94), the egrep -v "$SKIP_ERR" will exclude all lines from the sed output, so it will not have a chance to see any remaining ORA- errors.
ALSIZE=1
SKIP_ERR=""
sed -n $ALSIZE',$p' $ALERTLOG/alert_${ORACLE_SID}.log |\
egrep -v "$SKIP_ERR"|wc -l
0
SKIP_ERR="dummy"
sed -n $ALSIZE',$p' $ALERTLOG/alert_${ORACLE_SID}.log |\
egrep -v "$SKIP_ERR"|wc -l
15165
So you need to set your SKIP_ERR to something when it's not set or empty (if your skip file is empty). You haven't said if that is the case or shown the script output, but it seems to work apart from that.
Also not that if ALSIZE is zero sed isn't happy, at least in RHEL 5:
ALSIZE=0
SKIP_ERR="dummy"
sed -n $ALSIZE',$p' $ALERTLOG/alert_${ORACLE_SID}.log |\
egrep -v "$SKIP_ERR"|wc -l
sed: -e expression #1, char 4: invalid usage of line address 0
0
And when you test that the file exists at line 91, you show a message stating that and suggesting you'll stop there, but there is no exit after line 94; doesn't seem to be relevant here but seems like an oversight?
Below is the assignment for the bash shell script I'm writing. I'm having a
problem with -u information being output even though I am using the -f option.
This class is a beginner class, so please bear with me. Would be grateful to
have some input on my code. Thanks for taking the time to check this out if you
do.
Here is the sample output:
[***#***]$ chk3 -f share
share is a directory and it is readable | writable | executable | abecker is
currently logged in their home directory is /students/abecker
Here is the usage
chk -f filepath
If filepath exists, output in readable sentences
if it is a symbolic link, say so. You do not have to continue and report the
permissions.
if it doesn't exist, say so. Don't continue to report the permissions
report what it is: file, directory, or something else, and continue to
report the permissions:
report what combination of read, write and execute access rights your
program has for the data. Note that this is dependent on who runs your
program. Do not attempt to do this by looking at the permissions as output
by ls -l. You must use the test operators to do this.
If filepath does not exist (and is not a symbolic link), your program should
report this instead in an informative error message. In this case, you
should exit with an error.
chk -u user
If the user exists on the system, report
the path to the user's home directory
if the user is currently logged in, say so. Otherwise, report when they last
logged in. (Take some care so that this is generated reliably and quickly.)
If the user doesn't exist, report this in an informative error message, and
exit with an error.
Here is my code
#!/bin/bash
if [ $# -gt 2 ]
then
echo "only 2 aruments can be used"
exit 1
fi
if [ "$1" != '-f' -a "$1" != '-u' ]
then
echo "first argument must be -f or -u"
exit 1
fi
if [ "$1" = '-f' -a $# -ne 2 ]
then
echo 'Usage: chk -f [FILEPATH]'
exit 1
fi
if [ "$1" = '-f' ]
then
FILEPATH=$2
fi
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
if [ "$1" = '-u' -a $# -eq 1 ]
then
USER=$LOGNAME
elif [ "$1" = '-u' -a $# -eq 2 ]
then
USER=$2
fi
USERINFO=$(grep "^$USER:" /etc/passwd)
if ! grep "^$USER:" /etc/passwd > /dev/null
then
echo "$USER cannot be found on this system"
exit 1
fi
if ! who | grep "^$USER " > /dev/null
then
echo "$USER is not currently logged on and last logged on"
echo "$(last -1 "$USER")"
exit 0
else
echo "$USER is currently logged in their home directory is"
echo "$(echo "$USERINFO" | awk -F":" '{print $6}')"
fi
You're not putting the processing of different options into different blocks; the code simply passes through everything for all options.
e.g. for the -f option, you have:
if [ "$1" = '-f' ]
then
FILEPATH=$2
fi
and then process all the options for filepath, without putting them into the if statement, so if you pass in either -f or -u, it always passes into the code:
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif
If you don't want to break your program into functions, what you want to do is put all the code relating to processing the -f option into the same if-statement, somewhat like:
if [ "$1" = '-f' ]
then
FILEPATH=$2
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
fi # if [ "$1" = '-f' ]
Similarly for the -u option, you need to break it into multiple statements and then process all the options for the statement:
if [ "$1" = 'u' ]
then
if [ $# -eq 1 ]
then
USER=$LOGNAME
elif [ $# -eq 2 ]
then
USER=$2
fi
USERINFO=$(grep "^$USER:" /etc/passwd)
if ! grep "^$USER:" /etc/passwd > /dev/null
then
echo "$USER cannot be found on this system"
exit 1
fi
if ! who | grep "^$USER " > /dev/null
then
echo "$USER is not currently logged on and last logged on"
echo "$(last -1 "$USER")"
exit 0
else
echo "$USER is currently logged in their home directory is"
echo "$(echo "$USERINFO" | awk -F":" '{print $6}')"
fi
fi # if [ "$1" = '-u' ]
I would, however recommend putting the code that acts on the options into shell functions, which makes it much easier to read the code; e.g.
filepath() {
FILEPATH="$1"
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
}
And then for the processing code:
if [ "$1" = '-f' ]
then
filepath "$2"
fi
and something similar for the -u option.