Changing bash script countdown delay in 5 sec steps - linux

I have following command i use in a script as a countdown timer and it works great
# 30 sec Countdown Timer
for i in {30..1};do echo -n "$i." && sleep 1; done
Output:30.29.28.27.26 etc....
But I would like to be able to output in 5sec intervals
like
: 30.25.20.15 etc..
how can i change the script to do this ?

for i in {30..1..5};do echo -n "$i." && sleep 5; done

for i in {30..1}
do
if [ $((i%5)) == 0 ]
then
echo -n "$i."
fi
sleep 1
done

Here is straightforward script compatible with Dash and Bash:
i=30 && while [ $i -gt 0 ]; do sleep 5; i=$(($i-5)); printf "$i."; done

Related

Stop a loop process in shell scripting after some time

I have a script that looks like this, this script is checking whether my pods is in Running state or Not by redefining x on every loop.
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
until [ "$x" == "Running" ];
do
sleep 5
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
echo $x
done
But, I want to modify my current script to also support timeout, I mean after 60 second of looping, then it should be stop, or after 12 times of looping, then it should be stop. Any idea how to do that?
bash provides a special variable SECONDS that can be used as a rough timer.
SECONDS=0
while (( SECONDS < 60)); do
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
if [[ $x == Running ]]; then
break
fi
sleep 5
done
The expansion of SECONDS gives you not the assigned value, but the difference between the numbers of seconds since the assignment and the assigned value. The effect is like a variable whose value is incremented by 1 each second.
For timeout after 60 seconds try this Shellcheck-clean code:
#! /bin/bash -p
readonly kOC_SLEEP_SECS=5
readonly kOC_TIMEOUT_SECS=60
initial_secs=$SECONDS
while
status=$(/opt/oc get pods --selector app=bamboo.shortPlanName \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
printf '%s\n' "$status"
[[ $status != Running ]]
do
if (( (SECONDS - initial_secs) >= kOC_TIMEOUT_SECS )); then
echo 'ERROR: Timed out' >&2
exit 1
fi
sleep -- "$kOC_SLEEP_SECS"
done
I replaced app=${bamboo.shortPlanName} with app=bamboo.shortPlanName because the old code was causing Bash errors. You'll need to fix it properly.
See Why is printf better than echo? for an explanation of why I replaced echo with printf for printing the status.
The code treats a timeout as an error, and exits with bad status. You might want to do something different.
The actual timeout period will be somewhere between 60 and 65 (or maybe a little more) seconds. You'll need to do something different if you need a more accurate timeout.
For a timeout after 12 iterations try this Shellcheck-clean variation on the code above:
#! /bin/bash -p
readonly kOC_SLEEP_SECS=5
readonly kOC_MAX_ITERS=12
oc_iters=0
while
status=$(/opt/oc get pods --selector app=bamboo.shortPlanName \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
printf '%s\n' "$status"
[[ $status != Running ]]
do
if (( ++oc_iters >= kOC_MAX_ITERS )); then
echo 'ERROR: Timed out' >&2
exit 1
fi
sleep -- "$kOC_SLEEP_SECS"
done
I think trap is the easiest way to have an accurate timeout.
Prototype:
#! /bin/bash
HALT=0
TIMEOUT=4
# Trap for SIGALRM
stop_loop() {
HALT=1
}
# Set trap
trap stop_loop SIGALRM
# The timeout goes after $TIMEOUT seconds.
{ sleep $TIMEOUT && kill -SIGALRM $$ & }
# Main loop
until false || [[ $HALT -eq 1 ]]; do
sleep 1
echo 'loop'
done
echo 'out of loop'
exit 0
In your case, this looks a something like:
HALT=0
TIMEOUT=4
stop_loop() {
HALT=1
}
trap stop_loop SIGALRM
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
{ sleep $TIMEOUT && kill -SIGALRM $$ & }
until [ "$x" == "Running" ] || [[ $HALT -eq 1 ]];
do
sleep 5
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
echo $x
done
exit 0

time part of a script (running time)

With the time script.sh you will get the time it took to run a script, BUT if you want to time a part of the script?
let's say I want to test how long a loop takes, I could use the $SECONDS function, but is there any timer that counts milliseconds?
for example in the middle of a long code:
timerstart
until [[ $loop -eq 10000 ]]; do
((++loop))
echo "annoying"
done
timerstop
and then in the end of the script, I just add echo $timerresult , and it will display how many milliseconds it took to run only the selected code, and not the rest of the script
I'm looking for this solution so I can test parts of scripts for "slowness"..
is this possible to solve?
For Bash 5.0 and later, you can use $EPOCHREALTIME:
[...] it expands to the number of seconds since the Unix Epoch as a floating point value with micro-second granularity [...]
start=$EPOCHREALTIME
for ((i = 0; i < 10000; ++i)); do
echo "annoying"
done
stop=$EPOCHREALTIME
elapsed=$(bc -l <<< "$stop - $start")
You can use
date '+%s.%N'
to get the current time with nanosecond precision.
#!/bin/bash
start=$(date '+%s.%N')
until [[ $loop -eq 10000 ]]; do
((++loop))
echo "annoying"
done
stop=$(date '+%s.%N')
bc <<< $stop-$start
You can use time in the script for individual parts too, e.g. to time the loop:
time until [[ $loop -eq 10000 ]]; do
((++loop))
echo "annoying"
done
or with a brace group to time a group of commands:
time {
until [[ $loop -eq 10000 ]]; do
((++loop))
done
echo "Other commands here that are also timed"
}
Put the loop in a function.
#!/bin/bash
myloop() {
loop=1
until [[ $loop -eq 10000 ]]; do
((++loop))
echo "annoying"
done
}
time myloop

Is there a way to automatically restart a bash script? [duplicate]

For example, in the below script startover starts back from the top:
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
startover
Also keeping in mind exit 0 should be able to stop the script.
You could "recurse" using the following line:
exec bash "$0" "$#"
Since $0 is the path to the current script, this line starts the script without creating a new process, meaning you don't need to worry about too many restarts overflowing the process table on your machine.
Put it in a while loop. I'd also suggest you add a "sleep" so that you're not racing your machine's CPU as fast as it will go:
while true; do
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]]; then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
for i in {1..5}; do
echo $i
sleep 1
done
done
DO NOT USE WHILE LOOP at the start of the script since the condition below will exit the script and break the loop.
echo "Not a good time to transcode video!" && exit 0
You can try trapping the exit signal so that when the script exits it restarts
##########################################################################
## CHECK TIME
############bash##############################################################
trap '<path to script> ' EXIT
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
sleep 1;
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
sleep 1;
fi
##########################################################################
## CHECK TIME
##########################################################################
echo 1
echo 2
echo 3
echo 4
echo 5
startover
Note: I add a sleep of 1 second because this will give you the time to see message. trap the exit signal and re-running the script is acting like a while loop. I am also assuming that these codes are in a script.
How about enclosing the entire script in a while loop? For example,
while :
do
script
done
You may want to add a condition to break out of the loop.
This is not good practice, but what you asked for.
Put this at the end of your script. "$( cd "$( dirname "$0" )" && pwd )/$(basename $0)"

How to run command inside bash script only during specified period of time?

Let say, I need "command" to be run only if current time is from 11:10am till 2:30pm. How this can be done in bash script?
Something like below written in pseudo-language:
#!/bin/bash
while(1) {
if ((currentTime > 11:10am) && (currentTime <2:30pm)) {
run command;
sleep 10;
}
}
The other answers overlook that when a number starts with 0, Bash will interprete it in radix 8†. So, e.g., when it's 9am, date '+%H%M' will return 0900 which is an invalid number in Bash. (not anymore).
A proper and safe solution, using modern Bash:
while :; do
current=$(date '+%H%M') || exit 1 # or whatever error handle
(( current=(10#$current) )) # force bash to consider current in radix 10
(( current > 1110 && current < 1430 )) && run command # || error_handle
sleep 10
done
Could be shortened a bit if you accept a potential 10s delay for the first run:
while sleep 10; do
current=$(date '+%H%M') || exit 1 # or whatever error handle
(( current=(10#$current) )) # force bash to consider current in radix 10
(( current > 1110 && current < 1430 )) && run command # || error_handle
done
Done!
† Look:
$ current=0900
$ if [[ $current -gt 1000 ]]; then echo "does it work?"; fi
bash: [[: 0900: value too great for base (error token is "0900")
$ # oooops
$ (( current=(10#$current) ))
$ echo "$current"
900
$ # good :)
As xsc points out in a comment, it works with the ancient [ builtin... but that's a thing of the past :).
You could try something like :
currentTime=$(date "+%H%M")
if [ "$currentTime" -gt "1110" -a "$currentTime" -lt "1430" ]; then
# ...
fi
# ...
Or :
currentTime=$(date "+%H%M")
if [ "$currentTime" -gt "1110" ] && [ $currentTime -lt "1430" ]; then
# ...
fi
# ...
Or :
currentTime=$(date "+%H%M")
[ "$currentTime" -gt "1110" ] && [ "$currentTime" -lt "1430" ] && {
# ...
}
# ...
See man date for more details. You can also use a cron job to do more than run this script from 11:30.
NB : for your loop, you could use something like :
while [ 1 ]; do
#...
done
Or :
while (( 1 )); do
#...
done
You can create a 4-digit number describing the current time with date +"%H%M". I think that could be used to compare against other times (in 24h-format) numerically:
while [ 1 ]; do
currentTime=$(date +"%H%M");
if [ "$currentTime" -gt 1110 ] && [ "$currentTime" -lt 1430 ]; then
...
fi
sleep 10; # probably better to have this outside the if-statement
done
If you want to handle a timespan that includes midnight you just have to replace the && with ||.

Abort a program after a certain time

I'm trying to develop a test case for a program, and would like to fail the test case if it runs over 4 seconds.
How can this be done on linux? (I'm using Ubuntu)
I know I can time the execution and fail it time > 4, but that's just a bad approach.
Thanks!
(shell script solution)
Runs your testcase in background, get the PId of it, and checks after 4 seconds if the process is still running.
wait_seconds=4
interval_seconds=0.5
run_your_test_case &
pid=$!
max=`expr "$wait_seconds / $interval_seconds"`
for (( I=0; I<$max; I++ ));do
if kill -0 $pid >/dev/null;then
echo 'test failed'
else
echo 'test ok'
break
fi
sleep $interval_seconds
done
Final solution:
1 ./slowprogram.sh >/dev/null &
2 pid=$!
3 exitbreak=0
4 for c in {0..4}; do
5 sleep 1
6 kill -0 $pid 2>/dev/null
7 if [ $? -ne 0 ] ;then
8 exitbreak=1
9 break
10 fi
11 done
12 if [ $exitbreak == 1 ]; then
13 echo '[ OK ]'
14 else
15 echo '[FAIL]'
16 kill -9 $pid 2>/dev/null
17 fi
You could also do something like <command> & sleep 5 && kill ${!}

Resources