Capturing SSH Output in a variable in a bash script - linux

I'm trying to SSH into another machine and caputre it's ip address and hostname into a variable.
However, the varaible seems to be empty when i echo it.
I have tried out answers from other posts, but it didnt solve my problem. I'm not able to figure out as what the problem is.
#!/bin/bash
FILE=/home/admin/Vishal/output.txt
input=host.txt
while IFS= read -r line
do
echo "$line"
if [ $line = $HOSTNAME ]
then
ip=`hostname -i`
domain=`hostname -A`
host=`hostname`
sudo echo $ip $domain $line localhost >> $FILE
else
output=$(ssh -i -t admin#$line << "ENDSSH"
ip2=`hostname -i`
domain2=`hostname -A`
host2=`hostname`
ENDSSH
)
echo $output
fi
done <"$input"
The input file contains a list of hostnames
The variable FILE contains the path of the file where the results are to be stored.
The varaible output is the one in which i want to store the results.
Note: The script works for first part of the if where ssh isnt required.

Ony this command is relevant for your quesion:
output=$(ssh -i -t admin#$line << "ENDSSH"
ip2=`hostname -i`
domain2=`hostname -A`
host2=`hostname`
ENDSSH
)
The command sets some variables, but doesn't produce any output, so it's expected that output does't contain your values.
If you really want three lines with hostname related values, then something simple like this should work
output=$(ssh admin#$line "hostname -i; hostname -A; hostname")

Related

I have to read config file and after reading it will run scp command to fetch all details from the available servers in config

I have a config file that has details like
#pem_file username ip destination
./test.pem ec2-user 00.00.00.11 /Desktop/new/
./test1.pem ec2-user 00.00.00.22 /Desktop/new/
Now I need to know how can I fix the below script to get all the details using scp
while read "$(cat $conf | awk '{split($0,array,"\n")} END{print array[]}')"; do
scp -i array[1] array[2]#array[3]:/home/ubuntu/documents/xyz.xml array[4]
done
please help me.
Build your while read like this:
#!/bin/bash
while read -r file user ip destination
do
echo $file
echo $user
echo $ip
echo $destination
echo ""
done < <(grep -Ev "^#" "$conffile")
Use these variables to build your scp command.
The grep is to remove commented out lines.
If you prefer using an array, you can do this:
#!/bin/bash
while read -a line
do
echo ${line[0]}
echo ${line[1]}
echo ${line[2]}
echo ${line[3]}
echo ""
done < <(grep -Ev "^#" "$conffile")
See https://mywiki.wooledge.org/BashFAQ/001 for looping on files and commands output using while.

Ping multiple IP's without exiting from SSH using shell script

I have list of IP's which has to be pinged each other. Once I SSH to IP-1, i should ping all IP's in a loop before I come out of the loop.
I have tried the below..
for name in "${ip[#]}";
do
status=$(ssh -n -o LogLevel=QUIET -t -t -o StrictHostKeyChecking=no
ubuntu#$node ping -W 2 -q -c 5 $name")
if [ "$?" -eq "2" ]; then
echo -e "$(tput setab 7) $(tput setaf 1)$(date) $i unable to ping $name
$(tput sgr0)"
fi
done
This code is working. However every time it requires to do SSH, which is having a performance impact as i'm having more than 100 IP's in the list.
Can I get any help on this?
You could just make this list part of the command that you run on your target host, something like this:
ips=( "10.0.0.1" "10.0.0.2")
ssh serverName 'for i in '${ips[#]}'; do ping ${i} -c1; done'
Note the breaking single-quote to pass the array.
Edit:
Just to have it mentioned here: the tool "fping" is quite right for the job. It would give you just the list you asked for:
ips=( "10.0.0.1" "10.0.0.2")
ssh serverName 'fping -a '${ips[#]}' 2>/dev/null'
Cupcake is right about the possible problems that arise when you passing the list as suggested having entries containing whitespaces. In this special case, however, there are no whitespaces to be expected.
This should give you the List of IPs without fping
ips=( "10.0.0.1" "10.0.0.2")
ssh serverName 'for host in '${ips[#]}'; do if ping -c1 -w1 ${host} >/dev/null 2>&1; then echo ${host};fi;done'

Unable to array values outside of function in shell script [duplicate]

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:
#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output
if [ -z "$OUTPUT" ]
then
echo "Status WARN: No messages from SMcli"
exit $STATE_WARNING
else
echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
do
if [ "$STATUS" != "Optimal" ]
then
echo "CRIT: $NAME - $STATUS"
echo $((++XCODE))
else
echo "OK: $NAME - $STATUS"
fi
done
fi
echo $XCODE
I've tried using the following statement instead of the ++XCODE method
XCODE=`expr $XCODE + 1`
and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.
Because you're piping into the while loop, a sub-shell is created to run the while loop.
Now this child process has its own copy of the environment and can't pass any
variables back to its parent (as in any unix process).
Therefore you'll need to restructure so that you're not piping into the loop.
Alternatively you could run in a function, for example, and echo the value you
want returned from the sub-process.
http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL
The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.
Your specific example can be solved by rewriting the pipe to
while ... do ... done <<< "$OUTPUT"
or perhaps
while ... do ... done < <(echo "$OUTPUT")
This should work as well (because echo and while are in same subshell):
#!/bin/bash
cat /tmp/randomFile | (while read line
do
LINE="$LINE $line"
done && echo $LINE )
One more option:
#!/bin/bash
cat /some/file | while read line
do
var="abc"
echo $var | xsel -i -p # redirect stdin to the X primary selection
done
var=$(xsel -o -p) # redirect back to stdout
echo $var
EDIT:
Here, xsel is a requirement (install it).
Alternatively, you can use xclip:
xclip -i -selection clipboard
instead of
xsel -i -p
I got around this when I was making my own little du:
ls -l | sed '/total/d ; s/ */\t/g' | cut -f 5 |
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )
The point is that I make a subshell with ( ) containing my SUM variable and the while, but I pipe into the whole ( ) instead of into the while itself, which avoids the gotcha.
#!/bin/bash
OUTPUT="name1 ip ip status"
+export XCODE=0;
if [ -z "$OUTPUT" ]
----
echo "CRIT: $NAME - $STATUS"
- echo $((++XCODE))
+ export XCODE=$(( $XCODE + 1 ))
else
echo $XCODE
see if those changes help
Another option is to output the results into a file from the subshell and then read it in the parent shell. something like
#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
LINE="$LINE $line"
echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

grep statement in bash

I am using a for loop to connect to a list of servers and perform some simple commands. If the server is not accessible then stderr is written to a file. I then grep that file for the server name. It seems relatively simple and for some reason it isn't working. For troubleshooting purposes I have narrowed my server list to two servers and only run simple commands.
for i in $(cat serverlist)
do
nexec -i $i hostname 2>>errorlog.txt
if grep -q $i errorlog.txt; then echo "error accessing" $i
else echo "was able to connect to" $i
fi
done
So in the serverlist I have defined two incorrect hosts for troubleshooting purposes. Nexec tries to connect to each and perform the hostname command. If it is unable to connect an error message is printed to errorlog.txt
e.g.,
nexec: Error accessing host test1
Since both servers are incorrectly specified I am not able to connect to either. Again for troubleshooting purposes.
When grep runs the first time against $i which is the first server in the list it doen't find any matches in error.txt. However, it should. If I cat the results instead of grepping it is there.
I am actually doing this in bladelogic so the rules are a bit different. It should still work.
while read -r i <&3; do
nexec -i "$i" hostname 2>>"errorlog.$i.txt" || {
echo "nexec for $i exited with status $?" >&2
continue
}
# check for case where it claimed success but actually failed
# if nexec is written correctly, you don't need any of this logic
# ...and can completely remove the rest of the loop.
if grep -q -e "$i" "errorlog.$i.txt"; then
echo "error accessing $i" >&2
else
echo "was able to connect to $i" >&2
fi
done 3<serverlist
# and combine all the individual logs into one file:
cat errorlog.*.txt >errorlog.txt && rm -f -- errorlog.*.txt
Not familiar with nexec, but I imagine something like this is what you are looking for
for i in $(cat serverlist)
do
if [ ! "$(nexec -i $i hostname)" ]
then echo "error accessing" $i
else echo "was able to connect to" $i
fi
done

Bash - need to write data to a file based on output of a command in bash

I have a bash script which checks IP Addresses in a file line by line and executes a command with that IP saved in a variable .
I wanted to add a few lines of bash which would write that IP to a new file when the output of the command is something specific.
I would be grateful if anyone shed some light on this matter.
Assuming you stored the output of the command to a variable named output, you can do something like:
# $output is the output you got
# $expected is the expected value
# $IP is IP you just checked
if [ "$output" == "$expected" ]; then
echo $IP >> file.txt
fi
replace file.txt with the name of the file name you prefer.

Resources