command execute with error when use expect in shell script while work fine in pure shell - bash

I try to start all the exited docker containers deployed in separated servers, so basically i should execute the essential command below
[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)
It worked fine like in pure linux shell , but then error occured(show below) when i try to use expect to "send" the "essential" command. (local ip is 241,remote end is 209)
[root#localhost start_shell_dir]# spawn ssh root#192.168.1.209
root#192.168.1.209's password:
Last login: Fri Oct 15 22:23:25 2021 from 192.168.1.241
[root#localhost ~]# invalid command name "0"
while executing
"0 -ne 0 "
invoked from within
"send "[ 0 -ne 0 ] && docker start ""
The error log shows i have already log in the remote machine, and something wrong when i execute the docker command.
Glenn jackman from the comment area shows me the basic rule for tcl,then i realize expect does command substitutions before sending real command. We may see it from execute bash -x script.sh .
[root#localhost start_shell_dir]# bash -x startContainer.sh
+ read ip pass
+ read ip pass
+ /usr/bin/expect
[root#localhost start_shell_dir]# ++ docker ps -a
++ grep Exited
++ wc -l
++ docker ps -a
++ grep Exited
++ cut '-d ' -f1
spawn ssh root#192.168.1.209
root#192.168.1.209's password:
Last login: Fri Oct 15 22:37:56 2021 from 192.168.1.241
[root#localhost ~]# invalid command name "0"
while executing
"0 -ne 0 "
invoked from within
"send "[ 0 -ne 0 ] && docker start ""
Anyway, the final command that work for me is the command showed below(replace double quotes with braces and with backslash before $() to keep it as an ordinary character rather than pre-parse it).
send {[ \$(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start \$(docker ps -a | grep Exited | cut -d' ' -f1)}
#!/bin/bash
# my original script with error
while read ip pass
do
{
/usr/bin/expect <<-END
spawn ssh root#$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect "#"
send "[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)"
expect eof
END
}&
done<apps_ip.txt

Like the shell, Tcl (and expect) allows interpolation with double quotes. Tcl uses square brackets for command substitution (in the same way that the shell uses $(...))
Use curly braces to protect the contents of that string (analogous to the shell's single quotes):
send {[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)}
#....^.............................................................................................................^
# and don't forget to hit Enter
send "\r"
See https://www.tcl-lang.org/man/tcl8.6/TclCmd/Tcl.htm for the few syntax rules of Tcl.

Related

Bash is redirecting output from command only after script has finished

Context
Got a daft script that checks a process is running on a group of hosts, like a watchdog, as I say it's a daft script so bear in mind it isn't 'perfect' by scripting standards
Problem
I've ran bash -x and can see that the script finishes its first check without actually redirecting the output of the command to the file which is very frustrating, it means each host is actually being evaluated to the last hosts output
Code
#!/bin/bash
FILE='OUTPUT'
for host in $(cat /etc/hosts | grep webserver.[2][1-2][0-2][0-9] | awk {' print $2 ' })
do ssh -n -f $host -i <sshkey> 'ps ax | grep myprocess | wc -l' > $FILE 2> /dev/null
cat $FILE
if grep '1' $FILE ; then
echo "Process is NOT running on $host"
cat $FILE
else
cat $FILE
echo "ALL OK on $host"
fi
cat $FILE
done
Script traceback
++ cat /etc/hosts
++ awk '{ print $2 }'
++ grep 'webserver.[2][1-2][0-2][0-9]'
+ for host in '$(cat /etc/hosts | grep webserver.[2][1-2][0-2][0-9] | awk {'\'' print $2 '\''})'
+ ssh -n -f webserver.2100 -i <omitted> 'ps ax | grep myprocess | wc -l'
+ cat OUTPUT
+ grep 1 OUTPUT
+ cat OUTPUT
+ echo 'ALL OK on webserver.2100'
ALL OK on webserver.2100
+ cat OUTPUT
+ printf 'webserver.2100 checked \n'
webserver.2100 checked
+ for host in '$(cat /etc/hosts | grep webserver.[2][1-2][0-2][0-9] | awk {'\'' print $2 '\''})'
+ ssh -n -f webserver.2101 -i <omitted> 'ps ax | grep myprocess | wc -l'
+ cat OUTPUT
2
+ grep 1 OUTPUT
+ cat OUTPUT
2
+ echo 'ALL OK on webserver.2101'
ALL OK on webserver.2101
+ cat OUTPUT
2
+ printf 'webserver.2101 checked \n'
webserver.2101 checked
Issue
As you can see, it's registering nothing for the first host, then after it is done, it's piping the data into the file, then the second host is being evaluated for the previous hosts data...
I suspect its to do with redirection, but in my eyes this should work, it doesn't so it's frustrating.
I think you're assuming that ps ax | grep myprocess will always return at least one line (the grep process). I'm not sure that's true. I'd rewrite that like this:
awk '/webserver.[2][1-2][0-2][0-9]/ {print $2}' /etc/hosts | while IFS= read -r host; do
output=$( ssh -n -f "$host" -i "$sshkey" 'ps ax | grep "[m]yprocess"' )
if [[ -z "$output" ]]; then
echo "Process is NOT running on $host"
else
echo "ALL OK on $host"
fi
done
This trick ps ax | grep "[m]yprocess" effectively removes the grep process from the ps output:
the string "myprocess" matches the regular expression "[m]yprocess" (that's the running "myprocess" process), but
the string "[m]yprocess" does not match the regular expression "[m]yprocess" (that's the running "grep" process)

2nd echo statement in bash script not working

For some reason I'm not getting the output of my 2nd echo line when I run my script. Here is my code snippet:
IS_RUNNING=$(netstat -anp | grep ":7600" | grep java | awk '{print $7}' | cut -d"/" -f 2)
start(){
nohup /bin/su -c "/opt/app/bin/service start" - user &>/dev/null &
echo "Starting Services please wait"
sleep 30
if [ "$IS_RUNNING" = java ];
then
echo "Service is now running"
exit 0
fi
}
Interestingly. when I run it with:
sh -x ./service start
I get the output expected and my 2nd echo gets written to the screen.
+ case "$1" in
+ start
+ echo 'Starting Services please wait'
Starting Services please wait
+ sleep 30
+ nohup /bin/su -c '/opt/app/bin/service start' - user
+ '[' java = java ']'
+ echo 'Service is now running'
Service is now running
+ exit 0
Without using sh -x, I simply get this:
[root#init.d]# ./service start
Starting Services please wait
[root#init.d]#
I have a feeling I'm overlooking something simple. Can anyone help?
The running line is evaluated once:
IS_RUNNING=$(netstat -anp | grep ":7600" | grep java | awk '{print $7}' | cut -d"/" -f 2)
You should make a function for this or include this line in the start() function after the sleep.

system command returns strange value

I have this script
#!/bin/bash
npid=$(pgrep -f procname | wc -l)
echo $npid
if [ $npid -lt 3 ];then
service procname start;
fi
When procname is running, it works fine showing the correct number for $npid. It fails when there isn't any procname running. It should return zero, but it returns npid=3, exactly 3. The same problem I see for ps auxw | grep procname | grep -v grep | wc -l as well.
Something trivially wrong I just couldn't figure out, any suggestions ?
* EDIT
# This returns nothing if therisn't a process name poopit running
pgrep -f poopit
# In the case when no process running, below returns zero if typed on a bash cmd line
pgrep -f poopit | wc -l
# If running,
pgrep -f poopit | wc -l
17
# If running, the script $npid shows
19

Bash - output of command seems to be an integer but "[" complains

I am checking to see if a process on a remote server has been killed. The code I'm using is:
if [ `ssh -t -t -i id_dsa headless#remoteserver.com "ps -auxwww |grep pipeline| wc -l" | sed -e 's/^[ \t]*//'` -lt 3 ]
then
echo "PIPELINE STOPPED SUCCESSFULLY"
exit 0
else
echo "PIPELINE WAS NOT STOPPED SUCCESSFULLY"
exit 1
fi
However when I execute this I get:
: integer expression expected
PIPELINE WAS NOT STOPPED SUCCESSFULLY
1
The actual value returned is "1" with no whitespace. I checked that by:
vim <(ssh -t -t -i id_dsa headless#remoteserver.com "ps -auxwww |grep pipeline| wc -l" | sed -e 's/^[ \t]*//')
and then ":set list" which showed only the integer and a line feed as the returned value.
I'm at a loss here as to why this is not working.
If the output of the ssh command is truly just an integer preceded by optional tabs, then you shouldn't need the sed command; the shell will strip the leading and/or trailing whitespace as unnecessary before using it as an operand for the -lt operator.
if [ $(ssh -tti id_dsa headless#remoteserver.com "ps -auxwww | grep -c pipeline") -lt 3 ]; then
It is possible that result of the ssh is not the same when you run it manually as when it runs in the shell. You might try saving it in a variable so you can output it before testing it in your script:
result=$( ssh -tti id_dsa headless#remoteserver.com "ps -auxwww | grep -c pipeline" )
if [ $result -lt 3 ];
The return value you get is not entirely a digit. Maybe some shell-metacharacter/linefeed/whatever gets into your way here:
#!/bin/bash
var=$(ssh -t -t -i id_dsa headless#remoteserver.com "ps auxwww |grep -c pipeline")
echo $var
# just to prove my point here
# Remove all digits, and look wether there is a rest -> then its not integer
test -z "$var" -o -n "`echo $var | tr -d '[0-9]'`" && echo not-integer
# get out all the digits to use them for the arithmetic comparison
var2=$(grep -o "[0-9]" <<<"$var")
echo $var2
if [[ $var2 -lt 3 ]]
then
echo "PIPELINE STOPPED SUCCESSFULLY"
exit 0
else
echo "PIPELINE WAS NOT STOPPED SUCCESSFULLY"
exit 1
fi
As user mbratch noticed I was getting a "\r" in the returned value in addition to the expected "\n". So I changed my sed script so that it stripped out the "\r" instead of the whitespace (which chepner pointed out was unnecessary).
sed -e 's/\r*$//'

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