Getting an error for unexpected else in bash - bash

[SOLVED]
I'm pretty new tho bash-/shell-scripting and trying setup a check for ip address on a server which gets about once a week a new ip.
The script will then send the new ip to the users.
My problem is, that I'm getting a syntax-error in the last if-else statement for "unexpected" else and can wrap my head around why.
My first iteration didn't use functions, but instead one multi lined if-else which got me the same error. The functions on their own seem to work just fine.
#!/bin/bash
# script to send the new server ip to the users
# get the recent ip address of the system
new_ip=$(ip route get 8.8.8.8 | awk -F"src " 'NR==1{split($2,a," ");print a[1]}')
file=old_ip.txt
function ip_mail(){
source $file
if [ $new_ip != $old_ip ]
then
# email-address changed for obvious reasons
mail -s "New Server IP" [hidden]#[hidden].com <<< "$new_ip"
echo "old_ip=$new_ip" > old_ip.txt
exit 0
fi
exit 0
}
function set_old(){
touch old_ip.txt
echo "old_ip=$new_ip" > old_ip.txt
exit 0
}
if [ $file ]
then
ip_mail()
else
set_old()
fi

Related

Multi-line ssh where results need to be saved

I have a script where I have the following code:
results=$(ssh $server 'keys=$(redis-cli scan 0) ; keypairs="" ; for key in $keys ; do value=$(redis-cli get $key) ; keypairs="$keypairs$key $value\n" ; done ; echo $keypairs')
This code works, but it is rather ugly to look at so I am trying to perform this on multiple lines and I can't quite figure out how it should be done.
I have tried
results=$(ssh $server "
keys=$(redis-cli scan 0)
keypairs=\"\"
for key in $keys
do
value=$(redis-cli get $key)
keypairs=\"$keypairs$key $value\n\"
done
echo $keypairs
")
But this returns the error Could not connect to Redis at 127.0.0.1:6379: Connection refused. The weird thing is that the error shows up before it even has asked for the credentials for ssh:
Could not connect to Redis at 127.0.0.1:6379: Connection refused
Could not connect to Redis at 127.0.0.1:6379: Connection refused
host#server's password:
Can anyone tell me what is going on here and how I should go about having this section be a multi-line?
A multiline command can be used to assign to a variable, such as:
multilinecommand=$(test=check
if [ $test == "check" ]
then
echo "Yes"
else
echo "No"
fi
)
where echo $multilinecommand shows Yes.
If you however want to split a single command into multiple lines, you have to add a backslash at the end of the line to denote that the command isn't finished yet. Your command therefore becomes:
results=$(ssh $server " \
keys=$(redis-cli scan 0) \
keypairs=\"\"
for key in $keys
do
value=$(redis-cli get $key)
keypairs=\"$keypairs$key $value\n\"
done
echo $keypairs
")
because keys and keypairs are needed for the ssh command

Reply to IRC PING with PONG in bash script

So far I have a basic IRC Bot which I hope to be able to successfully PRIVMSG myself with but on the server I am testing on it requires a PONG response to PING.
I have absolutely no idea how to get around this. How do I reply to the PING?
My current code:
#!/bin/bash
function ircpreamble {
echo "NICK ${1}"
}
function privmsg {
TARGET=$1
sed -re "s/^(.*)\$/PRIVMSG ${TARGET} :\1/"
}
function delay {
while read LINE; do
sleep 2
echo $LINE
done
}
function messages {
msg=`cat pmmsg.txt`
echo $msg
}
function disconnect {
echo "QUIT goodbye :)"
}
(
nick=`cat randnick.txt`
pms=`cat pmnickname.txt`
ircpreamble "$nick";
messages | privmsg "$pms";
disconnect;
) | delay | nc irc.seersirc.net 6667
You first need to "catch" the server responses. This can be done like:
$ nc irc.seersirc.net 6667 |while read res;do echo "==>$res";done
==>:irc.seersirc.net NOTICE AUTH :*** Looking up your hostname...
==>:irc.seersirc.net NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
Since now you catch the response in a variable, you can filter out these responses and send the appropriate commands back to the server.
The problem is that when running netcat/telnet from a script a simple echo "PONG" is not sending the messages back to server but echo prints the messages locally in your terminal.
To send messages to the server you need either to pipe those messages to netcat in the beginning (the technique you already use) or to use some kind of expect script or to use a file to feed the netcat.
Regarding the PONG response, note that in order pong to be accepted by the server it must include the message sent by the server along with it's ping request.
Your server sends something like PING :D7AA1D1D (different every time) and thus the correct pong response is PONG :D7AA1D1D
As a result you can not just include a pong response in the first messages send in the beginning to netcat, since you don't know what is the ID that your server will sent to you along with the ping request.
This is a working draft script using a file to continuously feed the netcat:
rm .ircbot
touch .ircbot
prmnick="gv"
tail -f .ircbot |nc irc.seersirc.net 6667 |while read res
do
echo "==>$res"
if [[ "$res" == *"Couldn't resolve your hostname; using your IP address instead"* ]];then
sleep 2
echo "NICK gvgv" >>.ircbot
tail -n1 .ircbot #used just to display the last line of the helper file in my screen.
elif [[ "$res" == *"PING"* ]]; then
sleep 2
echo "$res" |sed 's/PING/PONG/' >>.ircbot
tail -n1 .ircbot
sleep 2
echo "USER gvgv 8 * :gvgv " >>.ircbot
tail -n1 .ircbot
sleep 2
echo "PRIVMSG $prmnick : hello from bot" >>.ircbot
tail -n1 .ircbot
fi
done
Tip: By opening a second terminal , you can manually "control" above bot by sending more commands to the .ircbot file (i.e $ echo "JOIN #channel" >>.ircbot) which will be also fed to netcat.
By the way, some web search about bash irc bots will return some useful results.
This is one easy bash script to use as an irc bot: https://github.com/Newbrict/bash-irc-bot/blob/master/bot.sh
Also , i found this useful IRC Over Telnet guide: http://archive.oreilly.com/pub/h/1963
Finally , this is an alternative using the /dev/tcp directly : https://gist.github.com/Wollw/3330337

What & How does this line of code work: {message:0:1} {message:23:17}

Would love for someone to help educate me on this line of code.
using diff command to compare two files which somehow allows for other commands such as message:0:1 and message:23:17 to access it's results.
How does this work?
message=$(diff previousscan.txt scan.txt | grep 192)
#get first char which indicates if the host came up or went away
iostring="${message:0:1}"
#get first ip-number from the list
computer="${message:23:17}"
#show ip-number in notify if host came up
if [ "$iostring" = \> ]; then
notify-send "$computer online"
fi
#show ip-number in notify if host went away
if [ "$iostring" = \< ]; then
notify-send "$computer offline"
fi
$message is not a command; it is a variable which holds the output of the diff command. The later lines reference substrings; ${message:0:1} is the first character (1 character starting at offset 0) of whatever is stored in $message.
A simple example to show the substring mechanism:
$ message="abcdefghijklmnop"
$ echo ${message:0:1}
a
$ echo ${message:7:3}
hij
The construction foo=$(bar) runs the command bar in a subshell, and places the output you would normally see in your terminal if you simply ran the command bar in the variable $foo.

expect loop / expect_out(buffer) issue

i'm new to expect. trying to write a script for cisco router that takes a list of interfaces as input, iterates over each interface, runs a command, saves output, counts number of lines in output and carries out if/else statements based on count.
I've searched on forums and pieced together the following script based on what other users have advised:
Just pasting code for the loop here for now:
foreach int $interfaces {
match_max 20000
send_user "\n"
send_user "====== Checking $int ======\n"
send_user "\n"
set capture [open output2 w]
expect "#"
send "show policy-map $int out\n"
sleep 1
expect "#"
set output $expect_out(buffer)
puts $capture $output
close $capture
set capture 0
send_user "\n\n---------- $expect_out(buffer) ------------\n\n"
set count 0
set count [exec cat output2 | wc -l]
send_user "\n=========== $int $count ===========\n"
if {$count<3} {
puts "Service policy on ! $ip ! $int ! is not working"
} else {
puts "Service policy on ! $ip ! $int is working"
}
exec echo > output2
}
However, when I run the script the file output2 and expect_out(buffer) always has just one character '#'. So i don't get the required results. For some reason, expect_out(buffer) isn't catching output of the command 'show policy-map $int out' and writing it to the file. I am guessing there's a fundamental coding mistake here in terms of the loop structure.
Help would be much appreciated!
Many thanks.
First off, read this book for expect learning: Exploring Expect
From above book:
All of the matched characters plus the characters that came earlier
but did not match are stored in a variable called expect_out(buffer).
In this case, it is nothing but your pattern which you are expecting and it is #. So what you see it right.
Now, for your requirement, please refer How to access the result of a remote command in Expect. This link has the necessary explanation for you.
Thank you again for your support. I found the issue. There was a '#' preceding the one i was interested in. Changed pattern match and now it works.

Code to count number of types of files on client in Unix

Hi I am new to shell scripting.
my requirement is:
There is one server & 3 clients. On each client error logs files are generated which are of 4 types.Say type 1 error , type 2 error like this type 4 error.
I want to write a script which read all the 3 clients from server & provide me number of times 4 different type of error logs are genereted on each client.
In short it should use ssh & grep command combination.
I have written the demo script but it's not providing me the number of times different type of logs occured on clients.
#error[1]='Exception: An application error occurred during an address lookup request, please contact IT'
#error[2]='SocketTimeoutException: Read timed out'
#error[3]='Exception: The search has produced too many matches to be returned'
#error[4]='Exception: No matching address found'
error_1='exception 1'
error_2='exception 2'
function get_list_of_clients()
{
NUM_OF_CLIENTS=$(wc -l ${CLIENT_IP_LIST} | awk -F " " '{ print $1 }' )
echo $NUM_OF_CLIENTS
if [ "${NUM_OF_CLIENTS}" -gt 0 ]
then
for ((row=2; row<=$NUM_OF_CLIENTS; row++))
do
CLIENTS_IP=$(sed -n ${row}p ${CLIENT_IP_LIST}| awk -F " " '{print $3 }')
echo ${CLIENTS_IP}
# get_number_of_errors
# copy_count_errors
echo ${$error_$row}
done
fi
}
function get_number_of_errors()
{
for((row_no=1; row_no<=4; row_no++))
do
{
/usr/bin/expect - <<- EndMark
spawn ssh root#${CLIENTS_IP} "grep $error[$row_no] var/error.log |wc -l" >> /tmp/${CLIENTS_IP}_error${row_no}.txt
match_max 50000
expect {
"*yes/no*" {
send -- "yes\r"
send -- "\r"
exp_continue
}
"*?assword:*" {
send -- "${CLIENT_PASSWORD}\r"
send -- "\r"
}
}
expect eof
EndMark
}
done
}
function copy_count_errors()
{
/usr/bin/expect - <<- EndMark
spawn scp root#${CLIENTS_IP}:/tmp/${CLIENTS_IP}* /tmp/
match_max 50000
expect {
"*yes/no*" {
send -- "yes\r"
send -- "\r"
exp_continue
}
"*?assword:*" {
send -- "${CLIENT_PASSWORD}\r"
send -- "\r"
}
}
expect eof
EndMark
}
get_list_of_clients
================================================================================
please help.
This is not really an answer, an attempt to help you getting your own.
The Problem
If I understand it correctly:
Your script runs on the server
You have three clients, each has log files
Your list of clients is in a file named $CLIENT_IP_LIST, where the IP is the third field and the first line is some sort of header, which you want to exclude.
Suggestions
It seems you need to ssh and scp to the clients. If possible, I suggest setting up SSH Public Key Authentication between your server and clients. This will greatly simplify your scripts.
Use the -C flag for compression with the scp and ssh commands.
You should copy the log files from the clients to the server and do processing on the server.
If you can choose a different scripting language such as Python, Tcl (You used Expect, which is really Tcl). Bash can handle your problem, but you will find other languages work better. This is my opinion, of course.
Get one piece of your puzzle to work before moving on to the next. For example, right now, your get_list_of_clients() function does not yet working.
That being said, here is a rewrite of get_list_of_clients, which I tested and it works within my assumptions about your $CLIENT_IP_LIST file:
function get_list_of_clients() {
let row=1
while read line
do
if (( row > 1 ))
then
set $line # Breaks line into pieces
CLIENT_IP="$3"
echo "$CLIENT_IP"
fi
let row=row+1
done < $CLIENT_IP_LIST
}

Resources