ssh when invoked with variables form while loop not working - bash

I am running into an issue where I am comparing two files (alert.txt and env.txt) and based on common value, I am pulling complete line data from env.txt based on matching entry. I am reading these values into while loop and then invoking a function as follows. the ssh call is not working and also the while loop inside start_admin not working
#!/bin/bash
start_admin()
{
ssh -n -f $user#$host "sh -c 'cd $domain; ./script.sh > /dev/null 2>&1'"
while !(netstat -na | grep -E $host:$port|grep -E LISTEN) 2>/dev/null
sleep 30
do
echo "waiting"
done
echo "started"
}
grep -Ff alert.txt env.txt | (while IFS=" " read -r r1 r2 r3 r4 r5
do
user=$r2
host=$r3
domain=$r4
port=$r5
done
start_admin $user $host $domain $port
)
and contents of alert.txt is:
env2
env3
and that of env.txt is :
env1 user1 host1 /app/domain1/ port1
env2 user2 host2 /app/domain2/ port2
env3 user3 host3 /app/domain3/ port3
I could solve this with multiple if else loops, but that is not a desired solution, please guide me in right direction as to what is missing ?

Use join instead of grep here to avoid false positives
Because your while read loop completes before you run start_admin, you only launch it once (done should be AFTER start_admin)
In start_admin, don't use $user, $host and so on, use $1, $2 (or use them but don't pass them as parameters when calling the function)
I'm not sure exactly what you try to achieve, but here is a revised version already.
#!/bin/bash
start_admin()
{
sanitized_domain=${domain//'"'/'\"'}
ssh -n -f "$user#$host" "sh -c 'cd \"$sanitized_domain\"; ./script.sh >/dev/null 2>&1'"
while ! netstat -na | grep -q " $host:$port .*LISTEN"; do
echo waiting
sleep 30
done
echo started
}
join alert.txt env.txt | while IFS=' ' read -r env user host domain port; do
start_admin
done
)

Related

Why is exit my status valid in command line but not within bash script? (Bash)

There are a few layers here, so bear with me.
My docker-container ssh -c"echo 'YAY!'; exit 25;" command executes echo 'YAY!'; exit 25; in my docker container. It returns:
YAY
error: message=YAY!
, code=25
I need to know if the command within the container was successful, so I append the following to the command:
docker-container ssh -c"echo 'YAY!'; exit 25;" >&1 2>/tmp/stderr; cat /tmp/stderr | grep 'code=' | cut -d'=' -f2 | { read exitStatus; echo $exitStatus; }
This sends the stderr to /tmp/stderr and, with the echo $exitStatus returns:
YAY!
25
So, this is exactly what I want. I want the $exitStatus saved to a variable. My problem is, I am placing this into a bash script (GIT pre-commit) and when this exact code is executed, the exit status is null.
Here is my bash script:
# .git/hooks/pre-commit
if [ -z ${DOCKER_MOUNT+x} ];
then
docker-container ssh -c"echo 'YAY!'; exit 25;" >&1 2>/tmp/stderr; cat /tmp/stderr | grep 'code=' | cut -d'=' -f2 | { read exitStatus; echo $exitStatus; }
exit $exitStatus;
else
echo "Container detected!"
fi;
That's because you're setting the variable in a pipeline. Each command in the pipeline is run in a subshell, and when the subshell exits the variable are no longer available.
bash allows you to run the pipeline's last command in the current shell, but you also have to turn off job control
An example
# default bash
$ echo foo | { read x; echo x=$x; } ; echo x=$x
x=foo
x=
# with "lastpipe" configuration
$ set +m; shopt -s lastpipe
$ echo foo | { read x; echo x=$x; } ; echo x=$x
x=foo
x=foo
Add set +m; shopt -s lastpipe to your script and you should be good.
And as Charles comments, there are more efficient ways to do it. Like this:
source <(docker-container ssh -c "echo 'YAY!'; exit 25;" 2>&1 1>/dev/null | awk -F= '/code=/ {print "exitStatus=" $2}')
echo $exitStatus

ssh instruction interrupt a while cycle?

I'm trying to deploy a cluster with a script which uses a yaml file. Except for an entry called "RaftFS" each yaml entry represents a machine to deploy. I don't understand why the script does only one while cycle if the ssh command is executed (even if the command is a simple ls !) but if I delete it then everything is fine and it does a number of cycle equals to the number of machines defined in the yaml file!
cat RaftFS/servers.yaml | shyaml keys-0 |
while read -r -d $'\0' value; do
if [ ! $value == "RaftArgs" ]; then
address=$(cat RaftFS/servers.yaml | shyaml get-value $value.machineIP | xargs -0 -n 1 echo)
username=$(cat RaftFS/servers.yaml | shyaml get-value $value.username | xargs -0 -n 1 echo)
password=$(cat RaftFS/servers.yaml | shyaml get-value $value.password | xargs -0 -n 1 echo)
#uploading my fingerprint (in order to use pssh)
echo $address $username $password
echo "uploading my fingerprint on $username#$address $password"
sshpass -p $password ssh-copy-id -oStrictHostKeyChecking=no $username#$address
echo "creating RaftFS"
ssh $username#$address echo "MACHINE=$value vagrant up">>vagrantscript.sh
fi
echo $address $username $password
done
I think there is no issue with the ssh command but it is a delimiter's issue
I've played a little with read -r -d $'\0' and these are the results
echo "a\0b\0c" | while read -r -d $'\0' var; do echo $var; done
prints
a
b
and
echo "a\0b\0c\0" | while read -r -d $'\0' var; do echo $var; done
prints
a
b
c
I assume there is some difference in the end line when the $value == "RaftArgs"
The standard input to the while loop is also the standard input to every command within the while loop. ssh reads from standard input in order to pipe the data to the remote command. It's probably consuming the data intended for the read statement.
You can redirect the ssh command's input:
ssh $username#$address ... >>vagrantscript.sh < /dev/null
Or you can run ssh with the "-n" flag to prevent reading from stdin:
ssh -n $username#$address ... >>vagrantscript.sh

How to read from 2 files

I try to make a script to connect with MySQL.
Reading hosts from one file and the MySQL password from another file, but I have a problem.
When I try to execute the script it's returning me this error:
./do: line 15: syntax error: unexpected end of file
The code is like this:
#!/bin/bash
FILE=$1
INFO=$2
cat $FILE | while read HOST;
cat $INFO | while read INFO;do
DBS=`mysql -u root -p $INFO -h $HOST --connect_timeout=4 -Bse'show databases' | wc -l`
if [ "$DBS" -gt "0" ]; then
echo $HOST - mysql - $DBS >> log.sql
fi
sleep 0.1
done
Where is my mistake ?
Salut DragoČ™,
You can't use two while loops (even after you fix the syntax error) to read from two different files at the same time.
Instead, you can use paste to combine the two files first, then execute your loop:
#!/bin/bash
hostnames="$1"
passwords="$2"
while IFS=$'\t' read host password; do
dbs=$(mysql -u root -p "$password" -h "$host" --connect_timeout=4 -Bse'show databases' | wc -l)
[ $dbs -gt 0 ] && echo "$host - mysql - $dbs" >> log.sql
done <<<"$(paste "$hostnames" "$passwords")"
This script will correctly handle filenames with spaces, as well as hostnames and passwords containing spaces.

iptables 4.12 IP not found: BASH function $line issue

While creating a simple script that grabs a blacklist of ip addresses and blocks them, I came across this issue:
## Function giving greif
function _droplist (){
while read line; do
$IPT -A droplist -i eth1 -s $line -j LOG --log-prefix "IP BlockList "
$IPT -A droplist -i eth1 -s $line -j DROP
done < $badlist ##IPT is /sbin/iptables
}
Through several iterations of this function I get the error:
Try `iptables -h' or 'iptables --help' for more information.
' not found.4.12: host/network `SO.ME.IPH.ERE
Running the same script with hard coded in IP's works fine, its either something to do with $line or m implementation of iptables.
cheers -- Baffled.
What does $badlist contain? A file name or a list of IPs?
if it's a filename it should work as you did it, but if it's a list of ip you have to change how you read them.
Assuming it's a new-line-delimited list of IPs like:
$ badlist="1.1.1.1\n2.2.2.2\n3.3.3.3"
$ echo -e "$badlist"
1.1.1.1
2.2.2.2
3.3.3.3
then you have to modify the loop as follows:
$ echo -e "$badlist"|while read line; do
# do stuff with $line
done
This was an early dive into bash scripting for me the code was also placed remotely on a friends box, the last rough iteration I own of it is on my pastebin:
#!/bin/bash
# ..
# ..
# ..
## Variables
stamp=$(date "+%d/%m/%Y %T")
seed="$RANDOM-$RANDOM-IPTABLES-$(date "+%d-%m-%Y")-TEMPORY" ## proverbial sandpit
log=/root/.IPTables.log; touch $log ## Always a logfile
dmp=/tmp/IPT_DUMP$seed.temp ## Intermediate
list=/tmp/IPT_LIST$seed.txt ## F**ing '\r\r\n' regex'rs
pos=0
## Link(s)
link=http://au.hive.sshhoneypot.com/downloads/iplist.php
## Log File
function _tolog (){
echo -e "$stamp - $#\r" >> $log
}
## Leadin'
_tolog " "
_tolog "-----Running rottweiler : A simple IP deny auto script "
sh -c "iptables --flush"; _tolog "--OK Tables have been flushed"; sleep 1
## Grab-blacklist(s) # Fortran array HO!
function _populate (){
wget $link -O $dmp | egrep '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' | xargs; _tolog "--OK blacklist stored from honeypot"
tr -d '\r' < $dmp > $list # See above rage comment
while read line; do
arrayIp[$pos]=$line
((pos++))
done < $list
_tolog "--OK IP array created!"
_tolog $(echo -e "---- Array size: ${#arrayIp[*]}")
for item in ${arrayIp[*]}
do
sh -c "/sbin/iptables -I INPUT -s $item -j DROP" # This drops the current blacklist
done; _tolog "--OK Commands passed to iptables DB" # Use: /sbin/iptables -L -v -n to get list back quickly ( no resolving crap )
/sbin/iptables-save > /root/iptables.backup; _tolog "--OK Table database saved to flatfile"
}
_populate
_tolog "-----Terminating script: Tables logged in ~/iptables.backup"
Had similar issues resulting from Windows line endings (\r\n). Converting to unix endings (\n) solved my problem.
Cheers, /phfff

Check if Tomcat is running via shell script

I need to check if Tomcat is running in my system via a shell script. If not I need to catch the process id and kill Tomcat. How shall it be achieved?
in order to get the running process, I've used this command:
ps x | grep [full_path_to_tomcat] | grep -v grep | cut -d ' ' -f 1
You have to be careful, though. It works on my setup, but it may not run everywhere... I have two installations of tomcat, one is /usr/local/tomcat on port 8080 and /usr/local/tomcat_8081 on port 8081. I have to use '/usr/local/tomcat/' (with the final slash) as the full_path because otherwise it would return 2 different pids if tomcat_8081 is running as well.
Here's the explanation of what this command does:
1) ps x gives you a list of running processes ordered by pid, tty, stat, time running and command.
2) Applying grep [full_path_to_tomcat] to it will find the pattern [full_path_to_tomcat] within that list. For instance, running ps x | grep /usr/local/tomcat/ might get you the following:
13277 ? Sl 7:13 /usr/local/java/bin/java -Djava.util.logging.config.fil
e=/usr/local/tomcat/conf/logging.properties [...] -Dcatalina.home=/usr/local/tomca
t [...]
21149 pts/0 S+ 0:00 grep /usr/local/tomcat/
3) As we get 2 entries instead of one due to the grep /usr/local/tomcat/ matching the pattern, let's remove it. -v is the invert-match flag for grep, meaning it will select only lines that do not match the pattern. So, in the previous example, using ps -x | grep /usr/local/tomcat/ | grep -v grep will return:
13277 ? Sl 7:13 /usr/local/java/bin/java -Djava.util.logging.config.fil
e=/usr/local/tomcat/conf/logging.properties [...] -Dcatalina.home=/usr/local/tomca
t [...]
4) Cool, now we have the pid we need. Still, we need to strip all the rest. In order to do that, let's use cut. This command removes sections from a FILE or a standard output. The -d option is the delimiter and the -f is the field you need. Great. So we can use a space (' ') as a delimiter, and get the first field, which corresponds to the pid. Running ps x | grep /usr/local/tomcat/ | grep -v grep | cut -d ' ' -f 1 will return:
13277
Which is what you need. To use it in your script, it's simple:
#replace below with your tomcat path
tomcat_path=/users/tomcat/apache-tomcat-8.0.30
pid=$(ps x | grep "${tomcat_path}" | grep -v grep | cut -d ' ' -f 1)
if [ "${pid}" ]; then
eval "kill ${pid}"
fi
One way to check by using wget for your server address and checking the status.
Check this link here :
http://www.velvettools.com/2013/07/shell-script-to-check-tomcat-status-and.html#.VX_jfVz-X1E
TOMCAT_HOME=/usr/local/tomcat-folder/
is_Running ()
{
wget -O - http://yourserver.com/ >& /dev/null
if( test $? -eq 0 ) then
return 0
else
return 1
fi
}
stop_Tomcat ()
{
echo "shutting down......"
$TOMCAT_HOME/bin/shutdown.sh
}
start_Tomcat ()
{
echo "starting......"
$TOMCAT_HOME/bin/startup.sh
}
restart ()
{
stop_Tomcat
sleep 10
kill_Hanged_Processes
start_Tomcat
sleep 60
}
the easy way to do that is :
ps -ef | grep tomcat
by using this command you'll get :
user [id-to-kill] Date [tomcat-path]
last step is killing the process
sudo kill -9 [id-to-kill]
Congratulation, your process was killed lOol
Tomcat's default port is 8080. u can grep it and use port status in comparision loop.
#!/bin/bash
STAT=`netstat -na | grep 8080 | awk '{print $7}'`
if [ "$STAT" = "LISTEN" ]; then
echo "DEFAULT TOMCAT PORT IS LISTENING, SO ITS OK"
elif [ "$STAT" = "" ]; then
echo "8080 PORT IS NOT IN USE SO TOMCAT IS NOT WORKING"
## only if you defined CATALINA_HOME in JAVA ENV ##
cd $CATALINA_HOME/bin
./startup.sh
fi
RESULT=`netstat -na | grep 8080 | awk '{print $7}' | wc -l`
if [ "$RESULT" = 0 ]; then
echo "TOMCAT PORT STILL NOT LISTENING"
elif [ "$RESULT" != 0 ]; then
echo "TOMCAT PORT IS LISTENINS AND SO TOMCAT WORKING"
fi
this way you can compare the script.you grep port 8080 if you are using the default port for tomcat.this will only check whether tomcat is running.
then you can check the processes using the port
lsof -i:8080 //if using port 8080
the if you want to free the port by killing the process using it use this command
kill 75782 //if for instance 75782 is the process using the port

Resources