3

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

1
  • Screen exits when the last program/window in it exits, so I believe your question boils down to having your shell prompt before exiting if it is the last screen window....?
    – Jeff Schaller
    Apr 11, 2018 at 10:23

3 Answers 3

3

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
}
0

I assume you are using bash.

are_u_sure(){
        read -n1 -p "Are you sure to exit? [y/N] "
        [ "$REPLY" != y ] && { echo; history -a; bash; }
}

trap are_u_sure EXIT

Add this to your ~/.bashrc, and then whenever you try to exit bash (type exit or ^D), you will be prompted. And you'll never quit unless you press 'y'.

This way, we don't have to stick to the screen magic since we can do it bash-wide.

As this approach is extremely simple, it does have a flaw, you will lose the variable assignments you typed in before. But may be we can work on that. You can modifiy this code to fit your needs anyway.

2
  • 1
    This technically quits bash but spawns a new interactive bash shell before it quits, leaving the original shell hanging in its EXIT signal handler until the new shell exits. Unconventional, but I suppose it works.
    – Kusalananda
    Apr 16, 2018 at 6:30
  • Yes. It works fine for me.
    – alzee
    Apr 16, 2018 at 6:40
0

I wasn't really satisfied with any other solution I saw out there so I ended up settling on this (added to .bashrc):

exit_with_prompt() {
    printf "Do you really want to exit screen? (y/n) "
    read REPLY
    case $REPLY in
        (y*) command exit "$@" ;;
    esac
}

export IGNOREEOF=0
[[ $(type -t exit) == "alias" ]] && unalias exit
[[ $(type -t logout) == "alias" ]] && unalias logout

if grep -q "^screen\b" /proc/$PPID/comm; then
    export IGNOREEOF=2
    alias exit=exit_with_prompt
    alias logout=exit_with_prompt
fi

And then this to .screenrc:

shell -$SHELL

This works sort of backwards from the way I'd normally do things. It unsets aliases and re-enables ctrl-d -> exit normally, but it checks if bash is running immediately inside a screen instance (specifically it checks if the start of the command of the parent process is "screen" then a word-boundary), then it disables ctrl-d/EOF to exit and sets up the exit with prompt aliases for both exit and logout.

I haven't been using it for very long but so far I'm quite satisfied with how it works.

You must log in to answer this question.

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