54

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
  • 9
    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
  • 1
    I always just hold down Cntl-C, generally works fine. Jan 22, 2010 at 18:41

6 Answers 6

48
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
  • 8
    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
40

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

7
  • 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
  • 1
    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, 2023 at 21:10
  • @Oliver: The OP is running a loop. That is "script-like". Feb 4, 2023 at 0:01
18
  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, 2023 at 21:11
10

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.

You must log in to answer this question.

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