netcat daemon for calling functions in sh script - shell

I'm new to shell programming and I have created a script that opens a connection to a server of mine. I want to have this script listen for an input from a client node and use that to run a function.
This is my process.
Run script > opens listener > on second computer use netcat to connect > run a function in the script on the server called nodefunction
I have server_port coded to '4444'
File name: run_hangman
nc -l -k -v -p 4444 | bash hangman
File name: hangman
#!/bin/bash
msg_timeout=0
host_ows=1
server_port=4444
dubOws=xxx.xxx.xxx.xxx
initServer() {
hostIP=`ip -o addr show dev "eth0" | awk '$3 == "inet" {print $4}' | sed -r 's!/.*!!; s!.*\.!!'`
hostOws=`echo $hostIP | cut -d . -f 4`
}
servermsg(){ #message //if = --n, echos on same line
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[SERVER] "$1
else
echo -n `date +"%T"` "[SERVER] "
fi
}
owsmsg(){ #message //if = --n, echos on same line
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[OWS] "$1
else
echo -n `date +"%T"` "[OWS] "
fi
}
playermsg() {
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[PLAYER] "$1
else
echo -n `date +"%T"` "[PLAYER] "
fi
}
question(){ #question, read, example
servermsg "$1"
if [ -n "$3" ]
then
servermsg "$3"
fi
read $2
echo ""
}
owsArray(){ #
for targetOws in $player_list
do
owsArray+=("OWS"$targetOws)
done
echo -n ${owsArray[*]}
echo
}
openSocket() {
servermsg "Starting the Game Listener"
servermsg "Opening Listener on port "$server_port
#nc -k -l $server_port |bash
#nc -kl -q 1 -p $server_port # This should create the listener & This is where everything stops.
servermsg "Now listening on port "$server_port
}
initServer
owsmsg "Starting server on OWS"$hostOws"..."
question "Enter all the OWSs that will play:" player_list "Example: 1 9 14 23"
echo $player_list
question "Type a category hint:" game_cat "Example: Type of Animal"
question "Type your word:" game_word "Example: zebra"
question "How many guesses:" game_guesses "Example: 7"
servermsg "OWS"$host_ows "has created a Hangman session"
servermsg "Players are:"; servermsg --n; owsArray
servermsg "Your word is "${#game_word}" letters long and players have "$game_guesses" guesses"
question "If this is all correct press enter, or CTRL+C to cancel"
openSocket
# I think I need a While script here to read the RAW input and run the playermsg function with the input?
I run the run_hangman file and then I connect to it via my node computer. I enter the following line and echo "1 2 3" because that is what I need. I also can't enter "1 2 3" directly into the window running "run_hangman" as if I press enter it goes to a new line.
echo "1 2 3" >/dev/tcp/xxx.xxx.xxx.xxx/4444
The server shows that it connected
Listening on [0.0.0.0] (family 0, port 4444)
14:52:24 [OWS] Starting server on OWS225...
14:52:24 [SERVER] Enter all the OWSs that will play:
14:52:24 [SERVER] Example: 1 9 14 23
Connection from [xxx.xxx.xxx.xxx] port 4444 [tcp/*] accepted (family 2, sport 41564)
Connection closed, listening again.
1 2 3
Now once it gets to openSocket it will allow me to send one more echo and then it closes on the server. I need to get what I presume is a while statement and have it listen for an input like "playermsg 'has started a game'" and have it actually run that function on the server.
Will I be able to get this to run, almost seems like it has to be in the background? I've been using nc(1) for reference and some websites said to try -d and that didn't work either.

I got it figured out. I did indeed need a while statement.
openSocket
while read -r value; do
val=${value:10}
if [[ "$value" == playermsg* ]]; then
val=${value:10}
playermsg $val
elif [[ "$value" == servermsg* ]]; then
val=${value:10}
servermsg $val
else
echo "Returned "$value
echo "Value was "$val
fi
done
So now on the second computer I simply run
echo "playermsg testing" >/dev/tcp/xxx.xxx.xxx.xxx/4444
Then the server displays the following:
15:29:36 [SERVER] Starting the Game Listener
15:29:36 [SERVER] Opening Listener on port 4440
15:29:36 [SERVER] Now listening on port 4440
15:29:37 [PLAYER] testing

Related

bash - using a command line argument (hostname) to run an external command

First time post, please forgive any missing information.
I have a script that is supposed to work with icinga. I need icinga to log into my Linux box and run a command like "script ". The script will then run a command to that hostname like sudo /etc/init.d/apache2 status then report back "running or unused" and an exit status of 0 or 2.
I'm wondering how I could add another command and have it one or the other run depending on what hostname it's given. Half of them need apache2 to be running and the other half need to have a process called dss to be running. I'd rather not have two separate scripts. Here is the working script and sorry it's sloppy but I haven't done any clean up and I'm not real good at bash yet.
so the user would run the script ./chkdss2 or
#!/bin/bash
ec=0
ec1=2
var3=run
var4=unused
for host in "$#"
do
var1=`ssh $host sudo /etc/init.d/dss status|awk '{print $6}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "$host is running"
echo $ec
else
echo "$host is not running"
echo $ec1
fi
done
There are a couple ways to test if a particular hostname is for apache or dss. You only need to have a list of hostnames for each case, and check if the received hostnames are included in said lists.
Method 1: using arrays
#!/bin/bash
# Method 1, using array lists of hosts
apachehosts=('ap1' 'ap2' 'ap3')
dsshosts=('dss1' 'dss2' 'dss3')
for host in "$#"
do
if printf '%s\n' "${apachehosts[#]}" | grep -Fxq "$host"
then
echo "$host: APACHE HOST"
elif printf '%s\n' "${dsshosts[#]}" | grep -Fxq "$host"
then
echo "$host: DSS HOST"
else
echo "ERROR, $host: unknown host"
fi
done
To modify the lists of hosts, simply add or remove values in the declaration of arrays apachehosts and dsshosts.
Method 2: using case
#!/bin/bash
# Method 2, using case
for host in "$#"
do
case "$host" in
'ap1'|'ap2'|'ap3')
echo "CASE, $host: APACHE HOST"
;;
'dss1'|'dss2'|'dss3')
echo "CASE, $host: DSS HOST"
;;
*)
echo "ERROR CASE, $host: unknown host"
;;
esac
done
Here, you edit the patterns in each case.
Method 3: using if
#!/bin/bash
# Method 3, using if
for host in "$#"
do
if [[ "$host" == 'ap1' || "$host" == 'ap2' || "$host" == 'ap3' ]]
then
echo "IF, $host: APACHE HOST"
elif [[ "$host" == 'dss1' || "$host" == 'dss2' || "$host" == 'dss3' ]]
then
echo "IF, $host: DSS HOST"
else
echo "IF, $host: unknown host"
fi
done
Here you modify the if conditions. I prefer the other methods, since this one is more complicated to edit, it is not as clear, especially if your list of hosts is long.
Method 4: condition on the hostnames
If you are lucky, there is some pattern to your hostnames. Ex. all apache servers start with letters ap, all your dss servers include dss in the name, ...
You can then simply use 2 if statements to decide which is which.
#!/bin/bash
# Method 4, patterns
for host in "$#"
do
if [[ $(echo "$host" | grep -c -e "^ap") -ne 0 ]]
then
echo "PATTERNS, $host: APACHE HOST"
elif [[ $(echo "$host" | grep -c -e "dss") -ne 0 ]]
then
echo "PATTERNS, $host: DSS host"
else
echo "PATTERNS, $host: unknown host"
fi
done
Note: hostname apdss1 would come out as an Apache server here. Previous methods would respond "unknown host". You patterns must be strict enough to avoid mismatches.
I had a similar task to get few report items using single ssh request.
I had to retrieve in singel ssh command:
Full hostname (FQDN)
Linux version
IP address of its Docker host if exist, or "none"
I got my script to work in 3 stage.
1. Get multiple lines of information from remote host
ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
'
Results:
2022-03-05:22:22:21
dudi-HP-Compaq-Elite-8300-MT
20
192.168.2.111 192.168.122.1 172.17.0.1
2. Process multiple lines of information from remote host
Read lines of information from remote host, into an array sshResultsArr.
readarray -t sshResultsArr < <(ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
Result:
dudi-HP-Compaq-Elite-8300-MT OS version: 20 has Docker IP: 172.17.0.1
3. Process each remote host in a loop
#!/bin/bash
for host in "$#"; do
readarray -t sshResultsArr < <(ssh -q $host <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
done
I was able to take a little bit from the answers I received and put together something that works well. Thank you all for your answers.
for host in "$#"
do
case "$host" in
('vho1uc1-primary'|'vho1uc2-backup'|'vho2uc1-primary'|'vho2uc2-backup'|'vho3uc1-primary'|'vho3uc2-backup'|'vho10uc1-primary')
var1=`ssh "$host" sudo /etc/init.d/apache2 status|awk '{print $4}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "Apache2 on $host is running"
echo "0"
else
echo "Apache2 on $host is not running"
echo "2"
fi
;;
*)
esac
done

Delete first line/chars on Windows Pipe

I'm trying to capture a live tcpdump from a custom designed linux system. The command I'm using so far is:
plink.exe -ssh user#IP -pw PW "shell nstcpdump.sh -s0 -U -w - not port 22 and not host 127.0.0.1" | "C:\Program Files\Wireshark\wireshark" -i -
This will fail since when executing the command on the remote system, this (custom) shell will output "Done" before sending data. I tried to find out a way to remove the "Done" message from the shell but doesn't appear to be any.
So I came up with this (added findstr -V):
plink.exe -ssh user#IP -pw PW "shell nstcpdump.sh -s0 -U -w - not port 22 and not host 127.0.0.1" | findstr -V "Done" | "C:\Program Files\Wireshark\wireshark" -i -
This works more or less fine since I will get some errors and the live capture will stop. I believe it might have something to do with the buffers, but I'm not sure.
Does anyone know of any other method of removing/bypassing the first bytes/chars of the output from plink/remote shell?
[edit]
as asked, nstcpdump.sh is a wrapper for tcpdump. as mentioned before, this system is highly customized. nstcpdump.sh code:
root#hostname# cat /netscaler/nstcpdump.sh
#!/bin/sh
# piping the packet trace to the tcpdump
#
# FILE: $Id: //depot/main/rs_120_56_14_RTM/usr.src/netscaler/scripts/nstcpdump.sh#1 $
# LAST CHECKIN: $Author: build $
# $DateTime: 2017/11/30 02:14:38 $
#
#
# Options: any TCPDUMP options
#
TCPDUMP_PIPE=/var/tmp/tcpdump_pipe
NETSCALER=${NETSCALER:-`cat /var/run/.NETSCALER`}
[ -r ${NETSCALER}/netscaler.conf ] && . ${NETSCALER}/netscaler.conf
TIME=${TIME:-3600}
MODE=${MODE:-6}
STARTCMD="start nstrace -size 0 -traceformat PCAP -merge ONTHEFLY -filetype PIPE -skipLocalSSH ENABLED"
STOPCMD="stop nstrace "
SHOWCMD="show nstrace "
NSCLI_FILE_EXEC=/netscaler/nscli
NSTRACE_OUT_FILE=/tmp/nstrace.out
NS_STARTTRACE_PIDFILE=/tmp/nstcpdump.pid
TRACESTATE=$(nsapimgr -d allvariables | grep tracestate | awk '{ print $2}')
trap nstcpdump_exit 1 2 15
nstcpdump_init()
{
echo "##### WARNING #####"
echo "This command has been deprecated."
echo "Please use 'start nstrace...' command from CLI to capture nstrace."
echo "trace will now start with all default options"
echo "###################"
if [ ! -d $NSTRACE_DIR ]
then
echo "$NSTRACE_DIR directory doesn't exist."
echo "Possible reason: partition is not mounted."
echo "Check partitions using mount program and try again."
exit 1
fi
if [ ! -x $NSCLI_FILE_EXEC ]
then
echo "$NSCLI_FILE_EXEC binary doesn't exist"
exit 1
fi
if [ -e $NSTRACE_OUT_FILE ]
then
rm $NSTRACE_OUT_FILE
echo "" >> $NSTRACE_OUT_FILE
fi
}
nstcpdump_start_petrace()
{
sleep 0.5;
$NSCLI_FILE_EXEC -U %%:.:. $STARTCMD >/tmp/nstcpdump.sh.out
rm -f ${NS_STARTTRACE_PIDFILE}
}
nstcpdump_start()
{
# exit if trace is already running
if [ $TRACESTATE -ne 0 ]
then
echo "Error: one instance of nstrace is already running"
exit 2
fi
nstcpdump_start_petrace &
echo $! > ${NS_STARTTRACE_PIDFILE}
tcpdump -n -r - $TCPDUMPOPTIONS < ${TCPDUMP_PIPE}
nstcpdump_exit
exit 1
}
nstcpdump_exit()
{
if [ -f ${NS_STARTTRACE_PIDFILE} ]
then
kill `cat ${NS_STARTTRACE_PIDFILE}`
rm ${NS_STARTTRACE_PIDFILE}
fi
$NSCLI_FILE_EXEC -U %%:.:. $STOPCMD >> /dev/null
exit 1
}
nstcpdump_usage()
{
echo `basename $0`: utility to view/save/sniff LIVE packet capture on NETSCALER box
tcpdump -h
echo
echo NOTE: tcpdump options -i, -r and -F are NOT SUPPORTED by this utility
exit 0
}
########################################################################
while [ $# -gt 0 ]
do
case "$1" in
-h )
nstcpdump_usage
;;
-i )
nstcpdump_usage
;;
-r )
nstcpdump_usage
;;
-F )
nstcpdump_usage
;;
esac
break;
done
TCPDUMPOPTIONS="$#"
check_ns nstcpdump
#nstcpdump_init
#set -e
if [ ! -e ${TCPDUMP_PIPE} ]
then
mkfifo $TCPDUMP_PIPE
if [ $? -ne 0 ]
then
echo "Failed creating pipe [$TCPDUMP_PIPE]"
exit 1;
fi
fi
nstcpdump_start
Regards

how to run multiple commands on a remote linux server using bash script

I am currently writing the following script that logs into a remote server and runs couple of commands to verify the performance of the server and prints a message based on the output of those commands .But the ssh doesn't work and returns the stats of the server that hosts the script instead .
Script
#!/bin/bash
#######################
#Function to add hosts to the array
#the following function takes the ip addresses provided while the script is run and stores them in an array
#######################
Host_storing_func () {
HOST_array=()
for i in $# ;do
HOST_array+=(${i});
done
#echo ${HOST_array[*]}
}
#######################
#Calling above function
#######################
Host_storing_func "$#"
############################################################
#Collect Stats of Ping,memory,iowait time test function
############################################################
b=`expr ${#HOST_array[*]} - 1 `
for i in `seq 0 $b` ;do
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh student35#${HOST_array[${i}]} << HERE
echo `hostname`
iowaittm=`sar 2 2|awk '/^Average/{print $5};'`
if [ $iowaittm > 10 ];then
echo "IO ==> BAD"
else
echo "IO ==> GOOD"
fi
memoryy=`free -m |grep Swap|awk '{if($2 == 0) print 0;else print (($4 / $2 ) * 100)}'`
if [ ${memoryy} < '10' ] ;then
echo "memory ==> good"
elif [[ "${memory}" -ge 0 ]] && [[ "${memory}" -le 10 ]];then
echo "No Swap"
else
echo "memory ==> bad"`enter code here`
fi
ping -w2 -c2 `hostname` | grep "packet loss"|awk -F, '{print $3}'|awk -F% '{print $1}'|sed 's/^ *//'|awk '{if ($1 == 0) print "Yes" ;else print "No"}'
HERE
done
Output : oc5610517603.XXX.com is the name of the source server
[root#oc5610517603 scripts]# ./big_exercise.sh 9.XXX.XXX.XXX 9.XXX.XXX.XXX
Pseudo-terminal will not be allocated because stdin is not a terminal.
oc5610517603.XXX.com
IO ==> GOOD
No Swap
ping: oc5610517603.ibm.com: Name or service not known
Pseudo-terminal will not be allocated because stdin is not a terminal.
oc5610517603.XXX.com
IO ==> GOOD
No Swap
ping: oc5610517603.XXX.com: Name or service not known
thanks for checking the script , I figured out a way to solve the problem
It is the sshpass command that is causing issue , you just have to put the opening HERE in single quotes if you want to use variables with in the HEREdoc but if the variables are calculated before ssh then you don't have to put opening HERE in single quotes
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh -T student35#${i} << 'HERE'
after I changed the sshpass command as above my script worked
I have modified your script a bit.
As suggested by #chepner, I am not using the Host_storing_func.
Heredocs for sshpaas are somewhat tricky. You have to escape every back-tick and $ sign in the heredoc.
Notice the - before the heredoc start, it allows you to indent the heredoc body. Also, try to avoid back-ticks when you can. use $(command) instead.
Hope it helps.
#!/bin/bash
#######################
#Function to add hosts to the array
#the following function takes the ip addresses provided while the script is run and stores them in an array
#######################
array=( "$#" )
user="student35"
############################################################
#Collect Stats of Ping,memory,iowait time test function
############################################################
for host in ${array[#]}; do
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh -l ${user} ${host} <<-HERE
thishost=\$(hostname)
echo "Current Host -> \$thishost";
iowaittm=\`sar 2 2|awk '/^Average/{print \$5}'\`
if [ \$iowaittm > 10 ]; then
echo "IO ==> BAD"
else
echo "IO ==> GOOD"
fi
memory=\$(free -m | grep Swap | awk '{if(\$2 == 0) print 0;else print ((\$4 / \$2 ) * 100)}')
if [ \${memory} < '10' ] ;then
echo "memory ==> good"
elif [[ "\${memory}" -ge 0 ]] && [[ "\${memory}" -le 10 ]]; then
echo "No Swap"
else
echo "memory ==> bad"\`enter code here\`
fi
ping -w2 -c2 \`hostname\` | grep "packet loss"|awk -F, '{print \$3}'|awk -F% '{print \$1}'|sed 's/^ *//'|awk '{if (\$1 == 0) print "Yes" ;else print "No"}'
HERE
done

Bash: how to make telnet or nc work in background until connection is closed

Below I am describing what I was struggling with and the decision that I came to currently. Please point me to the cleverer/smaller decisions, also would be glad to receive feedback.
So, there are a publisher and a client on a localhost, they communicate through port 8080. I can telnet or nc to this port and write output to a log normally, but cannot make the same commands work in background.
What I see is that when launched in background they stop immediately after getting the first input (is it really so?), but in foreground they work as they should and die only after publisher closes connection at this port.
This is what occurs normally:
> telnet localhost 8080 | tee output.log (or >>output.log, no matter)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Publisher starts sending information via port.
***some necessary output***
Publisher closes the port.
Connection closed by foreign host.
But when launched in background it stops immediately, without waiting for output:
> nohup telnet localhost 8080 | tee output.log (or <command> &, or nohup <command> &)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
You use a netcat command instead :
nohup nc -d localhost 8080 >>console-8080.txt &
If you want to indent the log file with the date :
nohup nc -d localhost 8080 | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-8080.txt; done &
To restart nc process in case it closes :
#!/bin/bash
ip=$1
port=$2
while [ 1 ]
do
nc -d $ip $port | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-$ip-$port.txt; done
sleep .1
done
Here is an expect script that I`ve come to. It is being launched in background.
nohup ./telnet_expect.sh &
The script spawns new bash session and performs usual redirection to a file.
Usually I arrange communicating between bash and expect via env variables, here I have not implemented it because the whole use case is small enough.
#!/bin/bash
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n"
logFile="/export/home/<user>/<folder>/output.log"
logFileExpect="/export/home/<user>/<folder>/expect.log"
echo > "$logFile"
echo > "$logFileExpect"
until nc -w 1 localhost 8080
do
sleep 1
done
expect -c "
log_user 1
set timeout 250
proc err_exit {msg} {
puts \"---\"
puts stderr \"\$msg\"
send \"exit status: \$?\r\"
exit 1
}
exp_internal -f /export/home/<user>/<folder>/expect.log 1
spawn bash
send \"telnet localhost 8080 >> /export/home/<user>/<folder>/output.log\r\"
sleep 10
expect {
\"*onnection closed by foreign host*\" {
send \"echo Success\r\"
}
timeout {
err_exit \"\nError: timeout\n\"
}
}
send \"exit\r\"
expect eof
"
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n"
Upd: below is more configurable version which takes log file as an argument. This is how to launch:
nohup ./telnet_expect.sh <your_output_log_file> &
The values of IP and port here are defined only once, so it is easy to move them to arguments as well. Also there are debug log and console log to understand what exactly is going on. User can provide either absolute or relative path to a log file.
#!/bin/bash
scriptFolderPath=$(dirname $(readlink -f "$0"))
logDir="$scriptFolderPath"
logFileDefault="$logDir"/"output_default.log"
logFileExpect="$logDir"/"expect.log"
ip="localhost"
port="8080"
logConsole="$logDir"/"console_telnet.log"
echo > "$logConsole"
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n" >> "$logConsole"
### debug info
logDebug="$logDir"/"debug_telnet.log"
echo > "$logDebug"
exec 5> "$logDebug"
BASH_XTRACEFD="5"
PS4='$LINENO: '
set -x
if [ -z "$1" ]; then
logFile="$logFileDefault"
else
if [[ $logFile =~ "/" ]]; then
logFile="$1"
else
echo "Got the log file in the same folder as the script" >> "$logConsole"
logFile="$scriptFolderPath"/"$1"
fi
fi
echo > "$logFile"
echo > "$logFileExpect"
### setting envs
echo -e "\nsetting BCKGR_TELNET_LOG" >> "$logConsole"
export BCKGR_TELNET_LOG="$logFile"
echo "set BCKGR_TELNET_LOG: $BCKGR_TELNET_LOG" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_EXPECT" >> "$logConsole"
export BCKGR_TELNET_LOG_EXPECT="$logFileExpect"
echo -e "set BCKGR_TELNET_LOG_EXPECT: $BCKGR_TELNET_LOG_EXPECT\n" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_PORT" >> "$logConsole"
export BCKGR_TELNET_LOG_PORT="$port"
echo -e "set BCKGR_TELNET_LOG_PORT: $BCKGR_TELNET_LOG_PORT\n" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_IP" >> "$logConsole"
export BCKGR_TELNET_LOG_IP="$ip"
echo -e "set BCKGR_TELNET_LOG_IP: $BCKGR_TELNET_LOG_IP\n" >> "$logConsole"
until nc -w 1 "$ip" "$port"
do
sleep 1
done
expect -c "
log_user 1
set timeout 250
proc err_exit {msg} {
puts \"---\"
puts stderr \"\$msg\"
send \"exit status: \$?\r\"
exit 1
}
puts \"Reading BCKGR_TELNET_LOG_EXPECT\"
if {[info exists env(BCKGR_TELNET_LOG_EXPECT)]} {
set bckgr_telnet_log_expect $::env(BCKGR_TELNET_LOG_EXPECT)
puts \"Found BCKGR_TELNET_LOG_EXPECT\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_EXPECT\"
}
puts \"Reading BCKGR_TELNET_LOG\"
if {[info exists env(BCKGR_TELNET_LOG)]} {
set bckgr_telnet_log $::env(BCKGR_TELNET_LOG)
puts \"Found BCKGR_TELNET_LOG\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG\"
}
puts \"Reading BCKGR_TELNET_LOG_PORT\"
if {[info exists env(BCKGR_TELNET_LOG_PORT)]} {
set bckgr_telnet_log_port $::env(BCKGR_TELNET_LOG_PORT)
puts \"Found BCKGR_TELNET_LOG_PORT\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_PORT\"
}
puts \"Reading BCKGR_TELNET_LOG_IP\"
if {[info exists env(BCKGR_TELNET_LOG_IP)]} {
set bckgr_telnet_log_ip $::env(BCKGR_TELNET_LOG_IP)
puts \"Found BCKGR_TELNET_LOG_IP\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_IP\"
}
exp_internal -f \$bckgr_telnet_log_expect 1
spawn bash
send \"telnet \$bckgr_telnet_log_ip \$bckgr_telnet_log_port >> \$bckgr_telnet_log\r\"
sleep 10
expect {
\"*onnection closed by foreign host*\" {
send \"echo Success\r\"
}
timeout {
err_exit \"\nError: timeout\n\"
}
}
send \"exit\r\"
expect eof
"
echo -e "\nUnsetting env variables" >> "$logConsole"
unset BCKGR_TELNET_LOG
unset BCKGR_TELNET_LOG_EXPECT
unset BCKGR_TELNET_LOG_PORT
unset BCKGR_TELNET_LOG_IP
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n" >> "$logConsole"

To Run multiple commands on different servers (taken from a text file) using shell script and writing the output to another file

Sorry for the trouble.. here is the code what works for a single server. I need help to loop it to multiple servers. Thanks in advance, Please help me out .
I need to know information of multiple servers like their:
Operating system info
Hostname and dns info
Network info
Who is online
Last logged in users and so on
logic is to pass the server names from a text file and display the same info for all the server in the file and write output to other file
Below are the different fucntions which fetch the details of a server. The same should be iterated for multiple servers..
#!/bin/bash
# grabsysinfo.sh - A simple menu driven shell script to to get information about your
# Linux server / desktop.
# Define variables
LSB=/usr/bin/lsb_release
# Purpose: Display pause prompt
# $1-> Message (optional)
function pause(){
local message="$#"
[ -z $message ] && message="Press [Enter] key to continue..."
read -p "$message" readEnterKey
}
# Purpose - Display a menu on screen
function show_menu(){
date
echo "---------------------------"
echo " Main Menu"
echo "---------------------------"
echo "1. Operating system info"
echo "2. Hostname and dns info"
echo "3. Network info"
echo "4. Who is online"
echo "5. Last logged in users"
echo "6. Free and used memory info"
echo "7. exit"
}
# Purpose - Display header message
# $1 - message
function write_header(){
local h="$#"
echo "---------------------------------------------------------------"
echo " ${h}"
echo "---------------------------------------------------------------"
}
# Purpose - Get info about your operating system
function os_info(){
write_header " System information "
echo "Operating system : $(uname)"
[ -x $LSB ] && $LSB -a || echo "$LSB command is not insalled (set \$LSB variable)"
#pause "Press [Enter] key to continue..."
pause
}
# Purpose - Get info about host such as dns, IP, and hostname
local dnsips=$(sed -e '/^$/d' /etc/resolv.conf | awk '{if (tolower($1)=="nameserver") print $2}')
write_header " Hostname and DNS information "
echo "Hostname : $(hostname -s)"
echo "DNS domain : $(hostname -d)"
echo "Fully qualified domain name : $(hostname -f)"
echo "Network address (IP) : $(hostname -i)"
echo "DNS name servers (DNS IP) : ${dnsips}"
pause
}
# Purpose - Network inferface and routing info
function net_info(){
devices=$(netstat -i | cut -d" " -f1 | egrep -v "^Kernel|Iface|lo")
write_header " Network information "
echo "Total network interfaces found : $(wc -w <<<${devices})"
echo "*** IP Addresses Information ***"
ip -4 address show
echo "***********************"
echo "*** Network routing ***"
echo "***********************"
netstat -nr
echo "**************************************"
echo "*** Interface traffic information ***"
echo "**************************************"
netstat -i
pause
}
# Purpose - Display a list of users currently logged on
# display a list of receltly loggged in users
function user_info(){
local cmd="$1"
case "$cmd" in
who) write_header " Who is online "; who -H; pause ;;
last) write_header " List of last logged in users "; last ; pause ;;
esac
}
# Purpose - Display used and free memory info
function mem_info(){
write_header " Free and used memory "
free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
ps auxf | sort -nr -k 4 | head -5
pause
}
# Purpose - Get input via the keyboard and make a decision using case..esac
function read_input(){
local c
read -p "Enter your choice [ 1 - 7 ] " c
case $c in
1) os_info ;;
2) host_info ;;
3) net_info ;;
4) user_info "who" ;;
5) user_info "last" ;;
6) mem_info ;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
}
# ignore CTRL+C, CTRL+Z and quit singles using the trap
trap '' SIGINT SIGQUIT SIGTSTP
First you need Key-Based SSH login to your remote servers (https://help.ubuntu.com/community/SSH/OpenSSH/Keys#Key-Based_SSH_Logins)
After that you use a loop like this:
function read_input(){
read -p "Enter your choice [ 1 - 7 ] " c
for server in $(cat your_server_file);
do
case $c in
1) os_info $server;;
2) host_info $server;;
3) net_info $server;;
4) user_info "who" $server;;
5) user_info "last" $server;;
6) mem_info $server;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
done
}
And in example your mem_info function
function mem_info(){
server=$1
write_header " Free and used memory "
ssh ssh_remote_user#server free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
ssh ssh_remote_user#server vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
ssh ssh_remote_user#server ps auxf | sort -nr -k 4 | head -5
pause
}
UPDATE
If you use sshpass you have to change a little bit.
Your your_server_file should look like this:
user1#password1|user1#server1
user2#password2|user2#server2
...
Your main function
function read_input(){
read -p "Enter your choice [ 1 - 7 ] " c
for line in $(cat your_server_file);
do
user_pass=$(echo $line | sed -e 's/\(.*\)|\(.*\)/\1/')
server=$(echo $line | sed -e 's/\(.*\)|\(.*\)/\2/')
case $c in
1) os_info $server $user;;
2) host_info $server $user;;
3) net_info $server $user;;
4) user_info "who" $server $user;;
5) user_info "last" $server $user;;
6) mem_info $server $user;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
done
}
And the mem_info function
function mem_info(){
server=$1
user_pass=$2
write_header " Free and used memory "
sshpass -p $user_pass ssh $server free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
sshpass -p $user_pass ssh $server vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
sshpass -p $user_pass ssh $server ps auxf | sort -nr -k 4 | head -5
pause
}
I did not quote anything. Please take care about special characters

Resources