3

I have a bash shell script. It writes out to a text file. Most of the it works find if I stop the script with a control-c at the command level. Sometimes the file that's been written to such as

echo "hello world" >myfile.txt

will end up being empty. So it it possible that when I hit control-c to stop the shell script running it is caught it at the instance where it's opening a write to the file and before it puts anything in it, it doesn't get the chance and leaves it empty?

If that's the case. What can I do in the bash shell script so that it will exit gracefully after it's written to the file and before it gets a chance to write to the file again, because it's doing this in a while loop. Thanks!

Jolta
  • 2,620
  • 1
  • 29
  • 42
Edward
  • 9,430
  • 19
  • 48
  • 71

1 Answers1

3

Yes, it's possible that you end up with an empty file.

A solution would be to trap the signal that's caused by ^C (SIGINT), and set a flag which you can check in your loop:

triggered=0

trap "triggered=1" SIGINT

while true
do
  if [ $triggered = 1 ]
  then
    echo "quitting"
    exit
  fi
  ...do stuff...
done

EDIT: didn't realize that even though the shell's own SIGINT handling will get trapped, it will still pass the SIGINT to its subprocesses, and they'll get killed if they don't handle SIGINT themselves.

Since echo is a shell builtin, it might survive the killing, but I'm not entirely sure. A quick test seems to work okay (file is always written, whereas without trapping SIGINT, I occasionally end up with an empty file as well).

As @spbnick suggests in the comments, on Linux you can use the setsid command to create a new process group for any subprocesses you start, which will prevent them from being killed by a SIGINT sent to the shell.

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Thanks for your helpful reply. So the above traps the control-c and what forces it to close the file it's opened? Is it the "exit" because it default to exit 0? – Edward May 13 '13 at 05:18
  • The responsibility for exiting the script is moved from the shell to the script itself (with the `if` statement). So if you're writing to a file in `do stuff`, it will just continue to do so; on the next run of the loop, the script checks if a ^C was caught and it exits nicely. – robertklep May 13 '13 at 05:21
  • Although I just noticed that the shell is still passing the SIGINT to its subprocesses. So it might not work after all :( – robertklep May 13 '13 at 05:25
  • 2
    Put the commands you don't wish to receive SIGINT from ^C into a separate process group. The easiest way to do it on Linux is with "setsid" command, which runs the command specified with its arguments in a new session and thus process group. Like this: `setsid program_writing_files file_towrite`. You can start another shell script or just `bash -c blah` like that. – spbnick May 13 '13 at 06:54