0

I have a shell script that loops over files. Processing each file can take several hours (it is CPU/GPU-limited). Sometimes I want to pause the execution at the end of a loop, when a file is finished processing, but then be able to resume later on, similar to how you can pause using ctrl-z and then fg, but I want it to finish the current loop before actually pausing/going into the background.

Basically I want the script, when it receives a ctrl-z (or similar), to wait until it reaches a certain line before it halts. How do you achieve that?

2
  • Why the complexity of signals when read -p 'Press enter to continue: ' seems sufficient? Commented Jul 22, 2023 at 19:52
  • @roaima What do you mean?
    – d-b
    Commented Jul 22, 2023 at 20:07

2 Answers 2

2

I'd use something like:

#!/bin/bash
function catch_sig ()
{
    interrupted=1
    trap catch_sig SIGUSR1
}

interrupted=0

trap catch_sig SIGUSR1
echo "kill -s SIGUSR1 $$ to pause" 
  
while [[ there is more to do ]] ; do
   long_running_task
   if [[ 1 -eq "$interrupted" ]] ; then
        interrupted=0
        read -p "You rang? " junk
    fi
done 

Then, in another terminal execute the kill -s SIGUSR1 on the PID it tells you.

Read man stty to see if there's a signal you want to use. Read man signal kill pkill bash.

4
  • Thank you. What does junk do?
    – d-b
    Commented Jul 23, 2023 at 5:03
  • @d-b: read wants a variable in which to store what it reads. Since I do not care what was typed, only that $USER has hit Enter. I never use the input, thus the choice of "junk" for the variable name
    – waltinator
    Commented Jul 26, 2023 at 5:22
  • Aha, I didn't know the syntax for read. Thank you.
    – d-b
    Commented Jul 26, 2023 at 19:44
  • @d-b: read is a "shell builtin", as shown by type read, explained in man bash. Please click the checkmark to "accept" my answer, if it solved your problem, and might help others.
    – waltinator
    Commented Jul 26, 2023 at 20:47
1

Solution

Basically I want the script, when it receives a ctrl-z (or similar), to wait until it reaches a certain line before it halts.

If I were you, I would make the certain line check for the existence of a certain file. If the file exists then the script should send SIGSTOP to itself.

[ -e /path/to/file ] && kill -s STOP "$$"

The reason I would use a file, not a signal, is you cannot (or at least you cannot easily) revoke a signal. With a file it's trivial; if you change your mind, just remove the file and the script wont stop itself.

After the script stops itself, send it SIGCONT and it will continue. In practice you will most likely run the script from an interactive shell with job control enabled; in such case the shell will detect when the script is stopped and you will be able to fg or bg the script at will.

It's up to you if the script tries to remove the file automatically or allows the file to remain and cause SIGSTOP in the next iteration(s) until you remove the file.

Note kill -s STOP "$$" is not equivalent to Ctrl+z. The keystroke causes the terminal (unless it's configured differently) to send SIGTSTP (not SIGSTOP) to the entire foreground process group. If you're sure the shell interpreting your script is the leader of its process group then you may want kill -s TSTP -- "-$$". Sending a signal to the group will make a difference if your script starts asynchronous processes that are also in the process group and you want to stop them as well. Processes may behave differently when they receive SIGSTOP (cannot be caught, blocked or ignored) or SIGTSTP (can be). Adjust the kill … command to your needs.


Proof of concept

The following script will stop itself or not, depending on the existence of /tmp/blocker, but it will do this only at the end of an iteration of the for loop, not in the middle.

#!/bin/sh -
for i in 1 2 3 4 5; do
   echo "processing $i"
   sleep 5
   echo "still processing"
   sleep 5
   echo "and processing some more"
   sleep 5
   echo "done processing $i"
   [ -e /tmp/blocker ] && kill -s STOP "$$"
done

1
  • Thank you, interesting solution and good motivation. Will wait a day and then mark it as the selected solution.
    – d-b
    Commented Jul 22, 2023 at 22:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .