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

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)"

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

How to record each command's duration/result and display this information with `history`?

I have some commands that take a really long time to run. I'd like to be able to keep track of all of them in history.
I've looked into history flags, but those seem to only show the time/date the command started and the status code.
Partially answering your question, here is a function I have in my .zshrc to send a desktop notification when a command took more than 10 s.
You may adapt to build your own history file.
(To have the notifications, you must have notify-send or adapt $notifier)
notifier=notify-send
if [[ ${TERM[1,5]} == xterm ]]; then
preexec() {
notifiable_cmd=$1
notifiable_start_time=`date +%s`
}
precmd() {
if (( $? == 0 )); then
notifiable_status="Success in "
else
notifiable_status="Failed in "
fi
if [[ "$notifiable_cmd" != "" ]]; then
(( notifiable_time = `date +%s` - $notifiable_start_time ))
if [[ $notifiable_time -ge 60 ]]; then
notifiable_str_time="$(($notifiable_time%100000/60)) min $(($notifiable_time%60)) s"
else
notifiable_str_time="$notifiable_time[1,5] s"
fi
if [[ $notifiable_time -gt 10 ]]; then
$notifier $notifiable_cmd "$notifiable_status $notifiable_str_time"
fi
fi
notifiable_cmd=
}
fi
(Adapted from this article Growl upon job completion in zsh on contrapunctus.net)

Bash Script not detecting failed exit codes

I can't get my bash script (a logging file) to detect any other exit code other than 0, so the count for failed commands isn't being incremented, but the successes is incremented regardless of whether the command failed or succeeded.
Here is the code:
#!/bin/bash
#Script for Homework 8
#Created by Greg Kendall on 5/10/2016
file=$$.cmd
signal() {
rm -f $file
echo
echo "User Aborted by Control-C"
exit
}
trap signal 2
i=0
success=0
fail=0
commands=0
read -p "$(pwd)$" "command"
while [ "$command" != 'exit' ]
do
$command
((i++))
echo $i: "$command" >> $file
if [ "$?" -eq 0 ]
then
((success++))
((commands++))
else
((fail++))
((commands++))
fi
read -p "$(pwd)" "command"
done
if [ "$command" == 'exit' ]
then
rm -f $file
echo commands:$commands "(successes:$success, failures:$fail)"
fi
Any help would be greatly appreciated. Thanks!
That's because echo $i: "$command" is succeeding always.
The exit status $? in if [ "$?" -eq 0 ] is actually the exit status of echo, the command that is run immediately before the checking.
So do the test immediate after the command:
$command
if [ "$?" -eq 0 ]
and use echo elsewhere
Or if you prefer you don't need the $? check at all, you can run the command and check status within if alone:
if $command; then .....; else ....; fi
If you do not want to get the STDOUT and STDERR:
if $command &>/dev/null; then .....; else ....; fi
** Note that, as #Charles Duffy mentioned in the comment, you should not run command(s) from variables.
Your code is correctly counting the number of times that the echo $i: "$command" command fails. I presume that you would prefer to count the number of times that $command fails. In that case, replace:
$command
((i++))
echo $i: "$command" >> $file
if [ "$?" -eq 0 ]
With:
$command
code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]
Since $? captures the exit code of the previous command, it should be placed immediately after the command whose code we want to capture.
Improvement
To make sure that the value of $? is captured before any other command is run, Charles Duffy suggests placing the assignment on the same line as the command like so:
$command; code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]
This should make it less likely that any future changes to the code would separate the command from the capture of the value of $?.

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 ||.

Tailing log until certain time threshold is reached

I'm tailing a log like this:
while [[ ! -n "${ready}" ]]; do
start_info=`grep "Ready" $LOG_FILE`
sleep 10
done
If the log file doesn't have "Ready" inside this goes on forever, how can I make it run for lets say 200 seconds? Like some kind of time threshold.
Thanks
This avoids repeatedly grepping the whole file:
start=$SECONDS
limit=200
while read -r line
do
if [[ $line =~ $ready ]]
then
start_info=$line
break
fi
if (( $SECONDS >= start + limit ))
then
break
fi
done < "$LOG_FILE"
If I got your question correctly (while [[ ! -n "${ready}" ]]; do is confusing) here is an example how you can check time threshold:
#!/bin/sh
...
timelimit=200
pausetime=10
while [[ -z "${start_info}" ]]; do
start_info=`grep "Ready" $LOG_FILE`
sleep $pausetime
timelimit=$((timelimit - $pausetime))
if [ $timelimit -le 0 ]; then
break
fi
done

Resources