# Exit while loop on execute script

**URL:** <https://community.unix.com/t/exit-while-loop-on-execute-script/342518>\
**Category:** Shell Programming and Scripting\
**Created:** [May 8, 2014, 12:55pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518 "2014-05-08T12:55:27Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![armatron](https://community.unix.com/letter_avatar/armatron/32/6_22734026db12803ebb3e059622ac3534.png) [@armatron](https://community.unix.com/u/armatron)\
**Post date:** [May 8, 2014, 12:55pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/1 "2014-05-08T12:55:27Z")

</div>

Hi,

I have first script which on IR remote command event execute the second script. If the second script is executed, it display echo "timeout expired" after 10s. This works as expected.  
But I also want to reset timer (increase time) in case if the second script is executed again within 10s.

The second script have a while loop with delay:

```nohighlight
time_delay=10
while [$time_delay -ge 0]
do
  sleep 1
  if [$time_delay -eq 0]
  then
     echo "timer expired"
     break
  fi

  time_delay=$(( $time_delay - 1 ))

done

```

The problem is, that the first script can't interrupt (reset timer) of second script when executing it when timer is not expired. The second script forces to finish the loop (echo "time expired") and after this it can be executed again.

Any suggestions please?

---

<div class="post-metadata">

**Author:** ![Scrutinizer](https://community.unix.com/user_avatar/community.unix.com/scrutinizer/32/1216_2.png) [@Scrutinizer](https://community.unix.com/u/Scrutinizer)\
**Post date:** [May 8, 2014, 1:35pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/2 "2014-05-08T13:35:40Z")

</div>

You could use a trap command, for example..

```nohighlight
trap 'reset_counter' ALRM
reset_counter() {
  time_delay=10
}
reset_counter
until [$time_delay -eq 0]
do
  sleep 1
  time_delay=$((time_delay-1))
done
echo "timer expired"

```

Now every time you send a `kill -ALRM` to the subprocess the counter is reset..

---

<div class="post-metadata">

**Author:** ![alister](https://community.unix.com/letter_avatar/alister/32/6_22734026db12803ebb3e059622ac3534.png) [@alister](https://community.unix.com/u/alister)\
**Post date:** [May 8, 2014, 1:53pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/3 "2014-05-08T13:53:08Z")

</div>

A simpler alternative:

```nohighlight
trap 'sleep 10' ALRM
sleep 10
echo timer expired

```

This behaves differently from Scrutinizer's version. While his will reset the timer to 10s, this version will add another 10s interval once the current interval expires. If this is acceptable, the simpler code may be a viable option.

Regards,  
Alister

---

<div class="post-metadata">

**Author:** ![armatron](https://community.unix.com/letter_avatar/armatron/32/6_22734026db12803ebb3e059622ac3534.png) [@armatron](https://community.unix.com/u/armatron)\
**Post date:** [May 8, 2014, 4:04pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/4 "2014-05-08T16:04:24Z")

</div>

Thanks!

I put code to my script but I can not kill subprocess ALRM, so I have the same problem: timer doesn't restart back to 10s on condition "rc1\_restart".

Here is my code:

```nohighlight
#!/bin/bash

def_sleep_time=10

if [$1 = "rc1_stop"]; then
   echo $(date) ': switch hdmi to rc1 stop' >> /home/pi/temp_rc
   def_sleep_time=0 #this should stop timer

elif [$1 = "rc1_restart"]; then
   echo $(date) ': switch hdmi to rc1 restart' >> /home/pi/temp_rc
   kill -ALRM # this should reset timer

fi

trap 'reset_counter' ALRM
reset_counter() {
  time_delay=$def_sleep_time
}

reset_counter

until [$time_delay -eq 0]
do
  sleep 1
  echo "timer coutdown: $time_delay" >> /home/pi/temp_rc
  time_delay=$((time_delay-1))

  if [$time_delay -eq 0]; then
     echo "timer expired" >> /home/pi/temp_rc
  fi
done

```

---

<div class="post-metadata">

**Author:** ![alister](https://community.unix.com/letter_avatar/alister/32/6_22734026db12803ebb3e059622ac3534.png) [@alister](https://community.unix.com/u/alister)\
**Post date:** [May 8, 2014, 4:37pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/5 "2014-05-08T16:37:11Z")

</div>

You need to send the signal to the proper process id.

If you are using a shell whose read builtin supports a timeout, you can use that. Instead of communicating via signals, use a fifo.

Regards,  
Alister

---

<div class="post-metadata">

**Author:** ![armatron](https://community.unix.com/letter_avatar/armatron/32/6_22734026db12803ebb3e059622ac3534.png) [@armatron](https://community.unix.com/u/armatron)\
**Post date:** [May 9, 2014, 1:38am UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/6 "2014-05-09T01:38:21Z")

</div>

I dont want to kill the whole script, because this would stop the timer and not restart it.

I assume I should kill only subproces "ALRM" (or whatever that means) to exit while loop and restart timer.

Is it possible to do that in the same script? What kind of signal should I sent to what process ID? kill -ALRM doesn't work.

If I run script by pressing a key on remote control, I got this (before timer expired):

```nohighlight
ps aux | grep switch_hdmi.sh

root 2784 0.0 0.1 1716 504 ? S 07:42 0:00 sh -c sudo sh /home/pi/switch_hdmi.sh rc1_restart
root 2785 6.0 0.4 3952 1656 ? S 07:42 0:00 sudo sh /home/pi/switch_hdmi.sh rc1_restart
root 2786 0.0 0.1 1716 528 ? S 07:42 0:00 sh /home/pi/switch_hdmi.sh rc1_restart
pi 2790 0.0 0.1 1976 616 pts/0 S+ 07:42 0:00 grep switch_hdmi.sh

```

---

<div class="post-metadata">

**Author:** ![Scrutinizer](https://community.unix.com/user_avatar/community.unix.com/scrutinizer/32/1216_2.png) [@Scrutinizer](https://community.unix.com/u/Scrutinizer)\
**Post date:** [May 9, 2014, 2:52am UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/7 "2014-05-09T02:52:51Z")

</div>

` ` If you take a look at the man page:

```nohighlight
NAME
     kill -- terminate or signal a process

SYNOPSIS
     kill [-s signal_name] pid ...
     kill -l [exit_status]
     kill -signal_name pid ...
     kill -signal_number pid ...

DESCRIPTION
     The kill utility sends a signal to the processes specified by the pid operand(s).

```

The kill command can be used to terminate a process or to signal it. The latter is what it is used for here. You'll notice the command also needs a pid (Process Id).

You can obtain the pid in the parent process by using the `$!` variable, right after you fire off a process in the background..

```nohighlight
somescript &
childpid=$!
echo $childpid
wait $childpid

```

---

<div class="post-metadata">

**Author:** ![armatron](https://community.unix.com/letter_avatar/armatron/32/6_22734026db12803ebb3e059622ac3534.png) [@armatron](https://community.unix.com/u/armatron)\
**Post date:** [May 12, 2014, 8:13am UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/8 "2014-05-12T08:13:05Z")

</div>

Thanks for your ideas. It finally works. Here is my script:

switch\_hdmi.sh:

```nohighlight
#!/bin/bash
echo $(date) ": executing switch hdmi... $1 with pid: $$" >> /home/pi/temp_rc

time_delay=5

# create temp file with the same name but extension .pid to store process ID of this running script
FILE=$0.pid

# if file doesnt exist then create it and store current pid. this should happen only for the first time of script executing
if [! -e $FILE]; then
   echo "creating new temp file $FILE and storing current pid: $$" >> /home/pi/temp_rc
   echo $$ > $FILE
fi

# kill previous process to restart timer
if ["$1" = "restart_timer"]; then
   echo "restarting timer. killing pid $(cat $FILE) from file: $FILE" >> /home/pi/temp_rc
   sudo kill -9 $(cat $FILE)
   exit 0
fi

# store pid of current process
echo "storing current pid: $$" >> /home/pi/temp_rc
echo $$ > $FILE

# switch hdmi ports
switch_hdmi(){
   echo "switch to hdmi port $1" >> /home/pi/temp_rc
}

if ["$1" = "rc1_stop"]; then
   echo $(date) ': switch hdmi to rc1 stop' >> /home/pi/temp_rc
   switch_hdmi 1
   time_delay=0 # stop timer

elif ["$1" = "rc1_restart"]; then
   echo $(date) ': switch hdmi to rc1 restart' >> /home/pi/temp_rc
   switch_hdmi 1

fi

until [$time_delay -eq 0]; do
  sleep 1
  echo "timer coutdown: $time_delay" >> /home/pi/temp_rc
  time_delay=$((time_delay-1))

  # execute if time expired
  if [$time_delay -le 0]; then
     echo "timer expired" >> /home/pi/temp_rc
     switch_hdmi 3
     exit
  fi
done
exit 0

```

I used temp file to store pid of current script execution. The next script execution kill pid from temp file (previous process).

Now i can execute script switch\_hdmi.sh from master script by the following command:

```nohighlight
sudo sh /home/pi/switch_hdmi.sh restart_timer; sudo sh /home/pi/switch_hdmi.sh rc1_restart &

```

Any suggestions are welcome.

---

<div class="post-metadata">

**Author:** ![sea](https://community.unix.com/user_avatar/community.unix.com/sea/32/2946_2.png) [@sea](https://community.unix.com/u/sea)\
**Post date:** [May 12, 2014, 4:35pm UTC](https://community.unix.com/t/exit-while-loop-on-execute-script/342518/9 "2014-05-12T16:35:04Z")

</div>

Not sure if i understood your script alltogether, but since you're asking for suggestions, i just had fun rewriting it...

Hope this helps

```nohighlight
#!/bin/bash
# http://www.unix.com/shell-programming-and-scripting/247133-exit-while-loop-execute-script-2.html
# Fun rewrite by sea
#
#	Variables
#
	TASK="$1" # Set argument as TASK
	ME=${0##*/} # basename
	LOG=$HOME/$ME.log # Logfile, to see what it does. (used to be tmp_rc)
	TMR=$HOME/$ME.timervalue	# Contains the current value of the timer
	PORT=$HOME/$ME.port # What is its default port?
	PID=$$ # PID of this execution of script
	TIMER_DELAY=5 # Default/Start value
	time_delay=$TIMER_DELAY # Work-value
#
#	Functions
#
	doLog() { # "MESSAGE STRING"
	# Prints current date time username,
	# followed by the supplied "message string"
		echo "$(date +'%F %T') $USER -- $1" >> $LOG
	}
	switch_hdmi(){ # NUM
	# switch hdmi ports
	# 
		doLog "switch to hdmi port $1"
		echo $1 > $PORT # This was probably your (missing) point ?
	}
#
#	Environment checks
#
	[-e $LOG] || ( touch $LOG ; doLog "Created logfile of $ME" )
	[-e $TMR] || ( touch $TMR ; echo $TIMER_DELAY > $TMR)
#
#	Argument handling
#
	doLog "executing switch hdmi... $1 with pid: $PID"
	case "$TASK" in
	restart_timer)	doLog "restarting timer. killing pid $PID"
			sudo kill -9 $PID # kill previous (now its current anyway) process to restart timer?
			exit 0 # Is this still required/working	?
			;;
	rc1_stop)	doLog "Switch hdmi to rc1 stop"
			switch_hdmi 1
			echo 0 > $TMR # stop timer
			;;
	rc1_restart)	doLog "Switch hdmi to rc1 restart"
			switch_hdmi 1
			echo $TIMER_DELAY > $TMR # reset timer
			;;
	*) doLog "Call with faulty args... What to do?"
			echo "Faulty arg supplied..."
			exit 1
	esac
#
#	Action / Timer
#
	until [$time_delay -eq 0] ; do
		sleep 1 # Actualy wait a second
		time_delay=$(cat $TMR) # Read timer value from file, if there was a change
		doLog "timer coutdown: $time_delay"	# Prints re-read timer value to log
		time_delay=$((time_delay-1)) # Update timer value
		echo $time_delay > $TMR # Update timer file
		if [$time_delay -le 0]
		then	# Time is up
			doLog "timer expired"
			switch_hdmi 3
			exit $?
		fi
	done
	exit 0

```
