49

I am rsyncing a few directories. I have a bash terminal open and am executing something like this:

for DIR in * ; do rsync -a $DIR example.com:somewhere/ ; done

However if I want to stop the whole things, I press Control-C. That stops the rsync, but then it keeps going to the next one. In this case I realize what has happened and then just press Control-C like a madman until things work again.

Is there some way to 'fix' this. I want it so if I have a loop like that, and press Control-C, that it will return me to my bash shell.

Amandasaurus
  • 30,211
  • 62
  • 184
  • 246

6 Answers6

43
for DIR in * ; do rsync -a $DIR example.com:somewhere/ || break; done

This will also exit the loop if an individual rsync run fails for some reason.

Kenster
  • 2,082
  • 16
  • 15
  • 7
    Additional tip: if you're in a nested loop, and want to completely terminate, use `break 2` (or replace "2" with the number of nested loops you want to terminate). – Dolan Antenucci Jan 23 '13 at 20:33
37

To expand on Dennis' answer, your code might look like:

trap "echo Exited!; exit;" SIGINT SIGTERM

For a working example (that happens to involve rsync), check out http://gist.github.com/279849.

  • Thanks, this is a generic solution that worked for me – wi1 Jul 30 '18 at 15:14
  • 1
    Best answer I think. Trapping both `SIGINT` and `SIGTERM` seems to more-consistently kill an infinite while loop than trapping `SIGINT` alone. UPDATE: eh, maybe not. I'm not sure. I still get some occasions where I have to hit Ctrl + C twice! I'm not sure why. – Gabriel Staples Dec 09 '21 at 19:31
28

You can set a trap for Control-C.

trap <command> SIGINT

will execute the command when Control-C is pressed. Just put the trap statement somewhere in your script at a point where you want it to become effective.

Ondra Žižka
  • 424
  • 2
  • 5
  • 14
Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • 1
    Example: to force an exit with code `2` whenever Ctrl + C is pressed: `trap 'exit 2' SIGINT` – Gabriel Staples Nov 18 '21 at 23:31
  • Even better: also print `Ctrl + C` just prior to exiting with return code `2`: `trap 'printf "%s\n" "Ctr + C"; exit 2' SIGINT` – Gabriel Staples Nov 18 '21 at 23:35
  • I can also confirm that `SIGINT` alone doesn't always seem to work to kill an infinite while loop. Doing `trap SIGINT SIGTERM`, [as this answer shows](https://serverfault.com/a/160961/357116), seems to work better. – Gabriel Staples Dec 09 '21 at 19:30
12
  1. Press Ctrl-Z to suspend the script ;
  2. kill %%

Credits, explanations and more details in this answer.

8

When you put a string of commands inside parentheses, the string will act as a single process, and will receive the SIGINT and terminate when you hit Ctrl-C:

(for DIR in * ; do rsync -a $DIR example.com:somewhere/ ; done)

But! In the case of the rsync command, it allows multiple sources, so the code you wrote would be better-written as:

rsync -a * example.com:somewhere/
amphetamachine
  • 832
  • 1
  • 8
  • 14
1

I tend to put another command in my loop that can easily be interrupted. It requires two ctrl-C's to be pressed.

for DIR in * ; do rsync -a $DIR example.com:somewhere/ ; sleep 1 ; done

It's not such a great solution for this rsync, which you probably want to run quickly. But it does work well for other loops, like this one:

while true ; do ping -c 10 example.com ; sleep 1 ; done

This loop will re-lookup the address of example.com every time through the loop, which is useful if you're watching for a DNS change.

Alan Porter
  • 186
  • 3