GNU Screen – How to Get Confirmation Before Exiting Screen

exitgnu-screen

How can I get a confirmation before I exit screen (when typing exit on the command-line). Is this possible?

Best Answer

I approached this by masking the exit command with a function; the function checks to see if you're within screen, and if you're the only child process left of that screen process.

exit() {
  if  [[ "$(ps -o pid= --ppid "$(ps -o ppid= -p "$$")" | wc -l)" -eq 1 ]]
  then
    read -p "Warning: you are in the last screen window; do you really want to exit? (y/n) "
    case $REPLY in
        (y*) command exit "$@" ;;
    esac
  else
    # not within screen at all, or not within the last screen window
    command exit "$@"
  fi
}

You would have to include that function as part of your (bash) profile, e.g. ~/.bash_profile. When starting screen, it will (unless told otherwise), start an instance of your $SHELL. That shell will be a child of a screen process. When exiting from a child shell, the above code checks to see how many processes are children of the current shell's parent process. From the inside out:

  • $(ps -o ppid= -p "$$") -- asks for the parent PID (adding the = suppresses the header line) of the current process ($$)
  • $(ps -o pid= --ppid ... | wc -l) -- asks for the list of PIDs (again without the header) whose parent PID is our parent, then counts the number of lines of output

If it looks like we're the last child process of a screen session, it prompts for confirmation; if the response begins with the letter y, the function calls the "real" exit command to exit the shell; otherwise, the function ends without exiting the shell.

If we're not the last child process, the function goes ahead and exits normally.

A couple notes as I developed this:

  • I initially had more tests in the if line to see if we are within a screen session, including seeing if STY is populated and for SHLVL being greater than 1. screen sets STY, and bash will increment SHLVL, but neither of those variables are read-only, so the test is not strong enough to be useful.

  • screen also sets a WINDOW variable, but checking it for 0 is not reliable; you could open two windows and then close window 0, leaving window 1 as the last window.

  • Entering EOF (usually Control+D) will, by default, cause the shell to immediately exit, bypassing this wrapper function. The best workaround I know would be to set the variable IGNOREEOF to some non-zero value; that will only delay the shell's inevitable exit, though.

Because I used so many bash- (and GNU procutils-) specific features above, I wanted to also provide a POSIX-conformant solution. The ps line changes to a ps ... | grep -c scheme in order to capture the number of processes with a specific parent PID. The other change is to re-work the read -p into a separate prompt and read.

exit() {
  parent="$(ps -o ppid= -p $$)"
  if [ "$( ps -eo ppid= | grep -c "^ *${parent}\$" )" -eq 1 ]
  then
    printf "Warning: you are in the last screen window; do you really want to exit? (y/n) "
    read REPLY
    case $REPLY in
        (y*) command exit "$@" ;;
    esac
  else
    # not within screen at all, or not within the last screen window
    command exit "$@"
  fi
}
Related Question