Bash infinite loop sleep having strange behavior (NGINX/PHP-FPM/PGSQL) - multithreading

I'm not sure it should be in stackoverflow or serverfault. I post here because it may be a programming problem.
I have this infinite loop:
#!/bin/bash
MESSAGE="XXX0"
RESULT=`curl "http://somepage.php?thread=0"`
while :
do
if [[ "$RESULT" == "DONE" ]]
then
RESULT=`curl "http://somepage.php?thread=0"`
elif [[ "$RESULT" == "NONE" ]]
then
sleep 5
RESULT=`curl "http://somepage.php?thread=0"`
else
printf "%s %s\n" "$(date --rfc-3339='seconds'): ELSE1-" "$RESULT" >> /var/log/XXX/loopXXX-`date --rfc-3339='date'`
sleep 5
RESULT=`curl "http://somepage.php?thread=0"`
if [[ "$RESULT" == "DONE" ]]
then
RESULT=`curl "http://jsomepage.php?thread=0"`
elif [[ "$RESULT" == "NONE" ]]
then
sleep 5
RESULT=`curl "http://somepage.php?thread=0"`
else
printf "STOP"
break
fi
fi
done
I have 3 loops doing the same job and requesting thread 0 to 2. In the DBtable the PHP page request, there is a column thread. So the three loops query the same table (read/write) but never the same lines.
The problem I experience is that in some nights (almost no activity), one loop doesn't request a page for several hours (I checked in NGINX access log). This only happen sometimes and the server is way more powerfull than needed yet.
Is there problems using infinite loop with curl? In total I have around 10 loops (different pages/tables) but they have a 10s sleep instead of 5s.
Is there a problem in my script with memory/curl? Have you ever experienced something similar?
THanks!

One of the curl lines is probably taking much longer than you expect to execute.
You should use curl's --max-time parameter in order to limit the duration of any single execution to something sane. It expects seconds.
e.g.
RESULT=`curl --max-time 10 "http://somepage.php?thread=0"`
Note that you may now encounter failures where instead you had been seeing long delays. Checking the output might be satisfactory for your application, but return codes are the path to enlightenment. You may even want to use the "-e" option in your shebang and/or create a handler to be used with a trap for ERR.

Try to set max timeouts for your every curl command to prevent them from hanging. Example:
curl -m 50 ...

Related

how to re-run a while loop by yes/no prompt, to search a file until it apears

I'm trying to write a script which helps me to search in log for a phrase "started".
The script that I have until now looks like this:
#!/bin/bash
greperg=
i=3
echo "Web server is going to be polled"
while
i=`expr $i - 1`
#polling
echo "Polling Nr. $i"
grep -q '^started$' log
greperg=$?
#test, if it goes on
test "$greperg" -gt 0 -a "$i" -gt 0
do
#waiting
echo 'waiting ...'
sleep 1
done
if test "$greperg" -eq 0
then
echo "Web server has started"
else
echo -n "Web server is not started"
fi
valid=''
while ((!valid)); do
echo - "Do you want to poll again? (J/N)"
read -t 5 answer
case "$answer" in
[Jj]) result=1; valid=1;;
[Nn]) result=0; valid=1;;
"") result=0; valid=1;;
*) valid=0 ;;
esac
done
echo
if ((result));then
: # ...............(repeat the process again, if its not found ask max 5 times)
else
echo "Timeout"
fi
exit 0
From line 38, I don't know how to re-run it, can anybody help?
What i'm looking for:
The polling should be expanded: If after the 3 attempts that word (started) is still not there, then ask the user with a (Y / N) query whether more should be polled 3 times or not.
The user should be asked a maximum of 5 times
(So a maximum of 3 × 6 = 18 times is polled).
At the very end please state what the status reached is (see example below).
polling ...
Web server has not started yet.
Wait ...
polling ...
Web server has not started yet.
Wait ...
polling ...
Web server has not started yet.
Should it be polled again (Y / N)? _ Y
polling ...
Web server has not started yet.
Wait ...
polling ...
Web server has not started yet.
Wait ...
polling ...
Web server has not started yet.
Should it be polled again (Y / N)? _ N
As requested, no further attempts are made.
Bottom line: web server has not started.
Your code has several odd designs. Bash should generally not need to use expr at all (the shell has built-in facilities for integer arithmetic and substring matching) and you usually want to avoid explicitly testing $?. I would break this up into functions to "divide and conquer" the problem space.
#!/bin/bash
# Print diagnostics to standard error; include script name
echo "$0: Web server is going to be polled" >&2
status=2
poll () {
local i
for ((i=1; i<=$1; ++i)); do
# Move wait to beginning of loop
if (($i > 1)); then
echo "$0: waiting ..." >&2
sleep 1
fi
echo "$0: Polling Nr. $i" >&2
# Just check
if grep -q '^started$' log; then
# return success
return 0
fi
done
# If we fall through to here, return failure
return 1
}
again () {
while true; do
# Notice read -p for prompt
read -t 5 -p "Do you want to poll again? (J/N)" answer
case "$answer" in
[Jj]*) return 0;;
"") continue;;
*) return 1;;
esac
done
}
while true; do
if poll 3
then
echo "$0: Web server has started" >&2
status=0
else
echo "$0: Web server is not started" >&2
status=1
fi
again || break
done
# Actually return the status to the caller
exit "$status"
The while true loop in the main script could easily be adapted to a for loop just like in the poll function if you want to restrict how many times the user is allowed to restart the polling. I wanted to show two different designs just to exhibit the options available to you.
In a real script, I would probably replace several of the simple if tests with the this || that shorthand. In brief,
this && that || other
is roughly equivalent to
if this; then
that
else
other
fi
with the difference that that if that fails, you will also trigger other in the shorthand case.
Perhaps notice also how ((...)) in Bash is an arithmetic context. The three-place for loop is also a Bash extension.

bash until not meeting condition [duplicate]

Wondering if it's possible to finagle this logic (checking a variable for changes over time and running a loop while true) into a bash if statement or while loop condition. I was hoping for something like:
var=$(du -h *flat*.vmdk)
var2=$(sleep 1 ; du -h *flat*.vmdk)
if [[ $var != $var2 ]]; then
while true
do
echo -ne $(du -h *flat*.vmdk)\\r
sleep 1
done
else
echo "Transfer complete"
fi
I've also played with a while loop, rather than an if then with no luck.
while [ $var != $var2 ] ; do echo -ne $(du -h *flat*.vmdk)\\r ; sleep 1 ; done
But I'm seeing that's not possible? Or I'm having issues where things are incorrectly getting expanded. I'm open to any solution, although I am limited by a very basic shell (ESXi Shell) where many common unix/shell tools may not be present.
You are doing while [ $var != $var2 ] but never updating any of these variables ..
I would do something like:
function get_size() {
echo $(du -h *flat*.vmdk)
}
var="$(get_size)"
sleep 1
var2="$(get_size)"
while [ $var != $var2 ]; do
var=$var2
var2="$(get_size)"
echo -ne "$(get_size)\\r"
sleep 1
done
echo "Transfer complete"
What it does:
Use a function, because when you have to write two times or more a same line, it should trigger a "I should make it a function" in your brain.
Updating $var and $var2 within the while loop, so you don't check the same exact values each time, but check diff between last value and current one.
Add newlines to your code, because code is done to be read by humans, not machines, humans does not likes one-liners :)
I've not tested it
Not a generic solution, but if what you need is to wait while file keeps on changing, you can simply monitor it's modification timestamp with find (taking that this command is available), like that:
while find . -name *flat*.vmdk -newermt $(date --date "-1 second" +#%s)|read
do
sleep 1
done
echo "Transfer Completed !"
w/o using any variables at all.
I like #zeppelin's approach and I think I would have used it, but the date command in my environment was limited and I wasn't looking to invest any more time trying to figure that out. I did go with Arount's solution with a few modifications as seen below:
get_size() {
echo $(du -h *flat*.vmdk)
}
update() {
var="$(get_size)"
sleep 2
var2="$(get_size)"
}
update
while [ "$var" != "$var2" ]; do
update
echo -ne "$(get_size)\\r"
sleep 1
done
echo "Transfer complete"
The changes I needed:
ESXi Shell uses sh/Dash so I wasn't able to use the proposed function get_size() {
For whatever reason, the variables always matched until I created the update function to run in and outside the while loop.
Works well/as expected now. Thank you everyone for your help...hope it helps someone else.

how can validate the time in a if condition in bash?

i hope that you can help me to give me ideas about my code, because i do not know how can i do for validate if the script enter two times followed in 20 minutes in the if condition.
rc=0
while [ $rc -ne 1 ]; do
do
ping 192.168.1.89
if [ "$?" -ne "0" ]; then
sleep 600 # this are 10 minutes
# here i need to validate if this script enters two times followed in 20 minutes
continue
else
continue
fi
done
any help will be welcome, thanks
It looks like you are monitoring an IP for loss of connectivity, and you want to take action if two pings 10 minutes apart fail. To achieve this you can just use a counter.
failures=0
rc=0
while [ $rc -ne 1 ]; do
do
ping 192.168.1.89
if [ $? -ne 0 ]; then
((failures++))
echo Ping failures: $failures
if (( failures < 2 )); then
sleep 600
else
echo Max failures hit
fi
else
failures=0
fi
done
You might need to modify the ping commandline to send a maximum number of pings (e.g. use -c 3 to send a maximum of 3). And you might want to redirect ping output to /dev/null.

Add seconds option to positional parameters in bash

Hello im writing a bash code which has some positional parameters but whats the best approach to add an optional seconds parameter which will allow some function to run for x seconds?
This is what code looks like:
doaction()
{
(run a process)
}
while [ $# -gt -0 ]; do
case "$1" in
--action|-a)
doaction ;;
--seconds|-s)
???????? $2
shift ;;
esac
shift
done
After x seconds kill process.
Also what happens when i run the script like
./script -s 10 -a
instead of
./script -a -s 10
Thanks
It looks like the timeout command is probably useful here. However, this only works on a script separate from the one that is currently running (as far as I can tell).
For your second question, the way you currently have things written, if you use ./script -a -s 10 then the result would be that the action would run before the delay is set. You can fix this by using a flag to indicate that the action should be executed, and you can ensure that the timeout is set (if at all) before the execution.
Here is my suggestion for a possible solution:
while [ $# -gt -0 ]; do
case "$1" in
--action|-a)
action=true;;
--seconds|-s)
time="$2"
shift;;
esac;
shift
done
if $action; then
timeout $time /path/to/action.sh
else
# do something else
fi
Where /path/to/action.sh is the location of the script that you want to run for a specific amount of time. You can test that the script exits after the specified number of seconds by replacing the script with the bash command top or something else which runs indefinitely.
You can use "getopts" to solve your problem. You might find the information on this link to be useful for your scenario.

Running a command only once from a script, even script is executed multiple times

I need some help. I'm having a script 'Script 1', which will call 'Script 2' to run in background which checks something periodically. But I want the Script 2 to get started only once, even Script 1 is called multiple times. Is there a way to do it?
It would be even more helpful, if someone suggests some commands to achieve this.
Thanks in advance
Sure, you can put something like this at the top of Script2:
if [[ -f /tmp/Script2HasRun ]] ; then
exit
fi
touch /tmp/Script2HasRun
That will stop Script2 from ever running again by using a sentinel file, unless the file is deleted of course, and it probably will be at some point since it's in /tmp.
So you probably want to put it somewhere else where it can be better protected.
If you don't want to stop it from ever running again, you need some mechanism to delete the sentinel file.
For example, if your intent is to only have one copy running at a time:
if [[ -f /tmp/Script2IsRunning ]] ; then
exit
fi
touch /tmp/Script2IsRunning
# Do whatever you have to do.
rm -f /tmp/Script2IsRunning
And keep in mind there's a race condition in there that could result in two copies running. There are ways to mitigate that as well by using the content as well as the existence, something like:
if [[ -f /tmp/Script2IsRunning ]] ; then
exit
fi
echo $$ >/tmp/Script2IsRunning
sleep 1
if [[ "$(cat /tmp/Script2IsRunning 2>/dev/null)" != $$ ]] ; then
exit
fi
# Do whatever you have to do.
rm -f /tmp/Script2IsRunning
There are more levels of protection beyond that but they become complex, and I usually find that suffices for most things.

Resources