50

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.

2
  • 7
    Dennis' answer is the right one, but you if you didn't do that, you don't have to 'press it like a madman', just hold it and let the keyboard repeat handle it :-) Jan 22, 2010 at 15:52
  • I always just hold down Cntl-C, generally works fine. Jan 22, 2010 at 18:41

6 Answers 6

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

1
  • 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). Jan 23, 2013 at 20:33
38

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.

3
  • Thanks, this is a generic solution that worked for me
    – wi1
    Jul 30, 2018 at 15:14
  • 2
    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. Dec 9, 2021 at 19:31
  • The OP said they were running in a shell, not a shell script. Therefore this will not work.
    – Oliver
    Feb 3 at 21:10
30

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.

6
  • 1
    Example: to force an exit with code 2 whenever Ctrl + C is pressed: trap 'exit 2' SIGINT Nov 18, 2021 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 Nov 18, 2021 at 23:35
  • I can also confirm that SIGINT alone doesn't always seem to work to kill an infinite while loop. Doing trap <cmd> SIGINT SIGTERM, as this answer shows, seems to work better. Dec 9, 2021 at 19:30
  • The OP said they were running in a shell, not a shell script. Therefore this will not work for the OP question.
    – Oliver
    Feb 3 at 21:10
  • @Oliver: The OP is running a loop. That is "script-like". Feb 4 at 0:01
14
  1. Press Ctrl-Z to suspend the script ;
  2. kill %%

Credits, explanations and more details in this answer.

3
  • This is great because it works "post facto" when you want to interrupt a long-running loop you already started. Sep 23, 2019 at 17:06
  • the best answer, thank you!
    – Kostanos
    Nov 30, 2019 at 17:51
  • This is the best solution because it works even if you forget to add the || break (@kenster answer) or the parentheses (@amphetamachine answer)
    – Oliver
    Feb 3 at 21:11
9

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/
1
  • This should be at the top. Cheers!
    – Ammaro
    Jan 26, 2022 at 19:17
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.