0

I want to write a bash script that will continue to run if the user is disconnected, but can be aborted if the user presses Ctrl+C.

I can solve the first part of it like this:

#!/bin/bash
cmd='
#commands here, avoiding single quotes...
'
nohup bash -c "$cmd" &
tail -f nohup.out

But pressing Ctrl+C obviously just kills the tail process, not the main body. Can I have both? Maybe using Screen?

Adrian Frühwirth
  • 42,970
  • 10
  • 60
  • 71
Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
  • 1
    As you mentioned screen: You can, of course, run your code inside a screen session and `bind` Ctrl+C to some code that exits your program (or just terminate the screen session with everything in it if you don't need graceful shutdown). Depending on the terminal emulator however that Ctrl+X will never even reach screen. – Johannes H. Nov 14 '13 at 00:33
  • Well the launching of the screen session would have to be within the bash script itself. (Basically, I want a single script that runs a lengthy update process, that frequently lasts longer than the user anticipates.) – Steve Bennett Nov 14 '13 at 00:34
  • That's no problem. Just run screen and let it run another instance of bash that executes the code. (for example `screen bash -c $cmd`). use the `-c` parameter of screen to specify a temporary file with the bind command in it to bind Ctrl+C. – Johannes H. Nov 14 '13 at 00:38

3 Answers3

1

This is probably the simplest form using screen:

screen -S SOMENAME script.sh

Then, if you get disconnected, on reconnection simply run:

screen -r SOMENAME

Ctrl+C should continue to work as expected

ishaaq
  • 6,329
  • 3
  • 16
  • 28
1

Fact 1: When a terminal (xterm for example) gets closed, the shell is supposed to send a SIGHUP ("hangup") to any processes running in it. This harkens back to the days of analog modems, when a program needed to clean up after itself if mom happened to pick up the phone while you were online. The signal could be trapped, so that a special function could do the cleanup (close files, remove temporary junk, etc). The concept of "losing your connection" still exists even though we use sockets and SSH tunnels instead of analog modems. (Concepts don't change; all that changes is the technology we use to implement them.)

Fact 2: The effect of Ctrl-C depends on your terminal settings. Normally, it will send a SIGINT, but you can check by running stty -a in your shell and looking for "intr".

You can use these facts to your advantage, using bash's trap command. For example try running this in a window, then press Ctrl-C and check the contents of /tmp/trapped. Then run it again, close the window, and again check the contents of /tmp/trapped:

#!/bin/bash

trap "echo 'one' > /tmp/trapped" 1
trap "echo 'two' > /tmp/trapped" 2

echo "Waiting..."
sleep 300000

For information on signals, you should be able to man signal (FreeBSD or OSX) or man 7 signal (Linux).

(For bonus points: See how I numbered my facts? Do you understand why?)

So ... to your question. To "survive" disconnection, you want to specify behaviour that will be run when your script traps SIGHUP.

(Bonus question #2: Now do you understand where nohup gets its name?)

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • Thanks, that's very useful. From testing your script, it seems that SIGINT is 'trapped', but still kills the script. Disconnecting 'traps' the SIGHUP, but the script keeps running. Is that expected? What would you do if you wanted to prevent SIGINT killing the script, for some reason? – Steve Bennett Nov 14 '13 at 11:29
1

I want to write a bash script that will continue to run if the user is disconnected, but can be aborted if the user presses Ctrl+C.

I think this is exactly the answer on the question you formulated, this one without screen:

#!/bin/bash
cmd=`cat <<EOF
# commands here
EOF
`
nohup bash -c "$cmd" &

# store the process id of the nohup process in a variable
CHPID=$!        

# whenever ctrl-c is pressed, kill the nohup process before exiting
trap "kill -9 $CHPID" INT

tail -f nohup.out

Note however that nohup is not reliable. When the invoking user logs out, chances are that nohup also quits immediately. In that case disown works better.

bash -c "$cmd" &
CHPID=$!
disown
Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
thom
  • 2,294
  • 12
  • 9
  • "When the invoking user logs out, chances are that nohup also quits immediately" - really? I've never seen this. What platform are you thinking of? – Steve Bennett Nov 14 '13 at 22:56
  • I had this on Debian, and Ubuntu. a nohupped program stays child of the shell that invoked it. When logging out of the shell, the children get terminated...the grandchildren, however, stay and become orphaned (and gathered by init) . Nohup did not work in a number of cases, like starting a program with nohup in the background aand logging out killed the program. Even (( prog & ) & ) did not work. I had to resort to disown the processes manually. – thom Nov 14 '13 at 23:08
  • Weird, I use Ubuntu mainly, haven't seen this. I normally do `nohup ./script.sh &`. Thanks for the warning though. – Steve Bennett Nov 15 '13 at 03:27
  • 1
    Whoa, warning to other users: if you're using this script, make sure you don't wrap it inside another `nohup`. I had one super script running a bunch of other scripts then this one, and nohupped the super script. The inner one is tailing nohup.out - which produces output to the outer one, which writes that output to nohup.out, which gets tailed...which produces more output... gack. – Steve Bennett Nov 17 '13 at 23:54