0

I have a script which has a few functions inside it which the main body uses to execute. Now, I want to run this script on 3 remote unix machines. Which is the neatest way to do this ? Most importantly, I don't to write a second script for remote connection. Everything should be inside this one script.

I've tried heredoc with ssh, which is not working because of the big functions !

Code -

#!/bin/bash

# Year Month Day Related functions
# FUNCTIONS
# Find no. of days in a year
yeardays()
{
# argument check
if [ X$1 = X ]
then
        read year
else
        year=$1
fi
# Check for leap years
if [ `expr $year % 400` = 0 ]
then
        echo 366
        exit
fi

if [ `expr $year % 100` = 0 ]
then
        echo 365
        exit
fi

if [ `expr $year % 4` = 0 ]
then
        echo 366
        exit
fi

echo 365
}

# Find no. of days in a Month
monthdays()
{
# argument check
if  [ X$1 = X ]
then
     read ymd   # year in yyyymmdd format
elif [ X$2 = X ]
then
      ymd=$1
else
      ymd=`expr \( $1 \* 10000 \) + \( $2 \* 100 \) + 1`
fi

year=`expr $ymd / 10000` ;
month=`expr \( $ymd % 10000 \) / 100` ;

case $month in
      1|3|5|7|8|10|12) echo 31 ; exit ;;
      4|6|9|11) echo 30 ; exit ;;
      *) ;;
esac

# except for month 2, which depends on whether the year is a leap year
# Use yeardays to get the number of days in the year and return a value
# accordingly.
daysInYear=`yeardays $year`

case $daysInYear in
   365) echo 28 ; exit ;;
   366) echo 29 ; exit ;;
esac
}

ymd2yd()    # convert from YYYYMMDD(gregorian) to YYYYDDD(julian)
{
# argument check
if [ X$1 = X ]
then
        read date
else
        date=$1
fi

year=`expr $date / 10000`
month=`expr \( $date % 10000 \) / 100`
days=`expr $date % 100`

count=1
while [ `expr $count \< $month` = 1 ]
do
        daysInMonth=`monthdays $year $count`
        days=`expr $days + $daysInMonth`
        count=`expr $count + 1`
done

julian=`expr \( $year \* 1000 \) + $days`
echo $julian
}

yd2ymd()    # convert from YYYYDDD(julian) to YYYYMMDD(gregorian)
{
# argument check
if [ X$1 = X ]
then
        read date
else
        date=$1
fi

year=`expr $date / 1000`
days=`expr $date % 1000`

month=1
while [ `expr $days \> 0` = 1 ]
do
        daysInMonth=`monthdays $year $month`
        days=`expr $days - $daysInMonth`
        month=`expr $month + 1`
done

days=`expr $days \+ $daysInMonth`
month=`expr $month \- 1`

gregorian=`expr \( $year \* 10000 \) + \( $month \* 100 \) + $days`
echo $gregorian
}

ydadd()     # Add/Subtract days to YYYYDDD format
{
# argument check
if [ X$2 = X ]
then
        difference=$1
        read yd     # Read the YYYYDDD format date
else
        yd=$1
        difference=$2
fi

days=`expr $yd % 1000`
year=`expr $yd / 1000`

days=`expr $days + $difference`
daysInYear=`yeardays $year`

while [ `expr $days \> $daysInYear` = 1 ]
do
        days=`expr $days - $daysInYear`
        year=`expr $year + 1`
        daysInYear=`yeardays $year`
done

while [ `expr $days \< 1` = 1 ]
do
        year=`expr $year - 1`
        daysInYear=`yeardays $year`
        days=`expr $days + $daysInYear`
done

yd=`expr \( $year \* 1000 \) + $days`       # Final date in YYYYDDD format
echo $yd
}

ymdadd()    # Add/Subtract days to YYYYMMDD format
{
if [ X$2 = X ]
then
        difference=$1
        read ymd    # Read YYYYMMDD format date
else
        ymd=$1
        difference=$2
fi

echo $ymd | ymd2yd | ydadd $difference | yd2ymd # Convert YYYYMMDD to YYYYDDD, perform date arithmetic, then revert to YYYYMMDD format
}

daysLeft()  # Calculate days between two dates in YYYYMMDD format
{
# argument check
if [ X$1 = X ]
then
        read ymd1   # First date in YYYYMMDD format
        read ymd2   # Second date in YYYYMMDD format
elif [ X$2 = X ]
then
        ymd1=$1
        read ymd2
else
        ymd1=$1
        ymd2=$2
fi

year1=`expr $ymd1 / 10000`
month1=`expr \( $ymd1 % 10000 \) / 100`
day1=`expr $ymd1 % 100`

year2=`expr $ymd2 / 10000`
month2=`expr \( $ymd2 % 10000 \) / 100`
day2=`expr $ymd2 % 100`

daysm1=`monthdays $year1 $month1`
days=`expr $daysm1 - $day1`
month1=`expr $month1 + 1`

while [ `expr $month1 \<= 12` = 1 ]
do
        daysm1=`monthdays $year1 $month1`
        days=`expr $days + $daysm1`
        month1=`expr $month1 + 1`
done

x=1
while [ `expr $x \< $month2` = 1 ]
do
        daysm2=`monthdays $year2 $x`
        days=`expr $days + $daysm2`
        x=`expr $x + 1`
done

days=`expr $days + $day2`
echo $days
}

# MAIN BODY
# Connect to different Servers
declare -a SERVER=('server1' 'server2' 'server3')
remoteUser=abc
serverNumbers=${#SERVER[@]}
count=0
while [ `expr $count \< $serverNumbers` = 1 ]
do
    # Connect to  server
    ssh -T -q $remoteUser@${SERVER[count]} <<-"END_TEXT"
    VALUE=`cat /home/cognos/cognos/c8/configuration/cogstartup.xml | grep -i xsd:long | head -1 | cut -d">" -f2 | sed 's/[:/<|crn:value]*//g'`
    VALUE_BACKUP=$VALUE
    let 'VALUE -= 30'       
    let 'VALUE *= 86400'
    RESULT1=`perl -e '@stats = stat("/home/cognos/cognos/c8/configuration/signkeypair"); print ((time - $stats[9]) < "$VALUE");'`
    RESULT2=`perl -e '@stats = stat("/home/cognos/cognos/c8/configuration/encryptkeypair"); print ((time - $stats[9]) < "$VALUE");'`
    RESULT3=`perl -e '@stats = stat("/home/cognos/cognos/c8/configuration/caSerial"); print ((time - $stats[9]) < "$VALUE");'`
    while [ "$RESULT1" -o "$RESULT2" -o "$RESULT3" ]
    do
            echo "Sending mail."
            # Calculate days left
            CURRENT_DATE=`date +"%Y-%m-%d" | sed 's/-//g'`
            CREATION_DATE=`ls -logE /home/cognos/cognos/c8/configuration/ | grep -i signkeypair | awk '{print $4}' | sed 's/-//g'`
            EXPIRY_DATE=`ymdadd $CREATION_DATE $VALUE_BACKUP`
            DAYS_LEFT=`daysLeft $CURRENT_DATE $EXPIRY_DATE`
            # Identify environment from hostname - DEV/UAT/PRD
            LOCALHOST=`hostname`
            ENVIRONMENT_TYPE=`echo $LOCALHOST | perl -ne '~m/.*([a-zA-Z]{3})[0-9]*$/; print $1;'|tr '[a-z]' '[A-Z]'`
            # Identify process - FormPF/GoReporting
            if (echo $LOCALHOST | grep -i cfp >/dev/null) then
                   PROCESS="FormPF"
            else
                   PROCESS="GoReporting"
            fi
            # Add details to mail body
            echo "The key was created on `ls -log /home/cognos/cognos/c8/configuration/ | grep -i signkeypair | awk '{print $4,$5,$6}'`, and was set to expire after $VALUE_BACKUP days." > mail.txt
            echo "Key expires in $DAYS_LEFT days !!" >> mail.txt
            echo " " >> mail.txt
            echo "The server is-" >> mail.txt
            hostname >> mail.txt
            echo " " >> mail.txt
            echo "Status of folders/files-" >> mail.txt
            echo " " >> mail.txt
            ls -log /home/cognos/cognos/c8/configuration/ | grep -i signkeypair | awk '{print $4,$5,$6,$7}' >> mail.txt
            ls -log /home/cognos/cognos/c8/configuration/ | grep -i encryptkeypair | awk '{print $4,$5,$6,$7}' >> mail.txt
            ls -log /home/cognos/cognos/c8/configuration/ | grep -i caSerial | awk '{print $4,$5,$6,$7}' >> mail.txt
        echo " " >> mail.txt
        echo "To reactive key, please restart the server at weekend with below options:" >> mail.txt
        echo " " >> mail.txt
        echo "/home/cognos/etc/restartAllServers.sh -cdsk" >> mail.txt
            # Send warning mail !!
            SUBJECT="!! ($ENVIRONMENT_TYPE) $PROCESS ($LOCALHOST) Cognos CSK(Common Symmetric Key) Expiry !!"
            EMAIL="xyz@abc.com"
            cat mail.txt | mailx -s "$SUBJECT" "$EMAIL"
        rm mail.txt
            break
    done
    logout
    END_TEXT
    count=`expr $count + 1`
done
exit 0
kaustav datta
  • 687
  • 2
  • 11
  • 31
  • good script, but how do you expect youre remotely executing script (via ssh), to find the functions you've defined. You can either install them on the remote machines, or send them 'over-the-wire' as you have done the the main body of your script. Beware that there are definite size limits to what ssh can pass an inline command AND that you can run into problem with variable expansion at the wrong time, which then requires speical quoting and escape chars, etc, etc. Good luck. – shellter Nov 02 '12 at 14:39
  • @shellter I've tried sending the entire script 'over-the-wire', in vain. Is it possible to use scp inside this script without getting into an infinite loop ? – kaustav datta Nov 02 '12 at 14:44
  • `if [ $( expr ... ) = 0 ]; then ...` makes my eyes hurt. Instead, use: `if expr ... > /dev/null; then ...` – William Pursell Nov 02 '12 at 16:49
  • 1
    Note that `<<-` only strips off leading **tab** characters, not arbitrary whitespace (see [the manual](http://www.gnu.org/software/bash/manual/bashref.html#Here-Documents)). – glenn jackman Nov 02 '12 at 20:48

1 Answers1

0

Move the functions inside the heredoc. It may require some changes to the code structure but it will work.

ssh app01.datacentre.private <<EOD
hostdatefunc () {
echo \$(hostname) \$(date)
}
hostdatefunc
EOD

You'll get the hostname and date on the remote server, not yours. Works. Stupid example, but proof of concept.

itsbruce
  • 4,825
  • 26
  • 35
  • heredoc can contain functions ? I thought I'd have to put the functions inside variables and then inside heredoc. What kind of changes are you talking about ? – kaustav datta Nov 02 '12 at 15:02
  • @kaustavdatta My example works, so functions work. You need to know what kind of shell is in use on the remote host, of course. As to changes - well, I didn't read your script in minute detail - if the functions are used outside the heredoc as well as inside it then you'd have to copy them into the heredoc, not move them. – itsbruce Nov 02 '12 at 15:06
  • 1
    You have to quote `EOD`, otherwise you will get the local hostname and date. – choroba Nov 02 '12 at 15:08
  • @kaustavdatta variables in a heredoc are expanded before the heredoc is read; everything else is just passed on STDIN to the program being invoked. So the functions are passed as lines of text to ssh, which runs them in a bourne-compatible shell (you hope) and so they work. – itsbruce Nov 02 '12 at 15:09
  • I have to use ssh -T otherwise I get this error - Pseudo-terminal will not be allocated because stdin is not a terminal. Why is this ?? – kaustav datta Nov 02 '12 at 15:14
  • @kaustavdatta without -T you'll get a warning message but it **will** work. Obviously, not having the warning is nicer ;) So try my solution and see if it works for you. Using **-T**, of course ;) – itsbruce Nov 02 '12 at 15:16
  • @itsbruce So if I write **ssh -T -q abc@wserver <<"EOF"** followed by my entire script body **EOF** it should work ? – kaustav datta Nov 02 '12 at 15:21
  • It should. Be careful with quoting ;) – itsbruce Nov 02 '12 at 15:24
  • @kaustavdatta Any expansions could be a problem. If you are really going to put **all** the code into the heredoc, then quoting the delimiter, as recommended by choroba, will stop any expansions from happening at your end - postponing them all to the remote side. – itsbruce Nov 02 '12 at 15:43