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
}
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
}
我通过
exit
用函数屏蔽命令来解决这个问题;该函数检查您是否在屏幕内,以及您是否是该屏幕进程剩下的唯一子进程。您必须将该功能包含在您的(bash)配置文件中,例如
~/.bash_profile
. 启动屏幕时,它将(除非另有说明)启动您的 $SHELL 实例。该外壳将是屏幕进程的子进程。当从子 shell 退出时,上面的代码检查有多少进程是当前 shell 的父进程的子进程。从里到外:$(ps -o ppid= -p "$$")
-- 询问=
当前进程的父 PID(添加抑制标题行)($$
)$(ps -o pid= --ppid ... | wc -l)
-- 询问其父 PID 是我们父级的 PID 列表(同样没有标题),然后计算输出的行数如果看起来我们是屏幕会话的最后一个子进程,它会提示确认;如果响应以字母开头
y
,函数调用“real”exit
命令退出shell;否则,函数在不退出 shell 的情况下结束。如果我们不是最后一个子进程,则函数继续执行并正常退出。
我开发这个时有几点注意事项:
我最初在该
if
行中进行了更多测试,以查看我们是否在屏幕会话中,包括查看是否STY
已填充以及SHLVL
是否大于 1。屏幕设置 STY,而 bash 将增加 SHLVL,但这些变量都不是只读的,所以测试不够强大,没有用。screen 也设置了一个
WINDOW
变量,但检查它0
是不可靠的;您可以打开两个窗口,然后关闭 window0
,将 window1
作为最后一个窗口。默认情况下,输入 EOF(通常是Control+ D)会导致 shell 立即退出,绕过这个包装函数。我知道的最好的解决方法是将变量设置
IGNOREEOF
为某个非零值;不过,这只会延迟 shell 不可避免的退出。因为我在上面使用了很多 bash-(和 GNU procutils-)特定功能,所以我还想提供一个符合 POSIX 的解决方案。该
ps
行更改为一个ps ... | grep -c
方案,以捕获具有特定父 PID 的进程数。另一个更改是将 重新工作read -p
为单独的提示和read
.我假设您正在使用 bash。
将此添加到您的 ~/.bashrc 中,然后每当您尝试退出 bash(键入 exit 或 ^D)时,系统都会提示您。除非你按'y',否则你永远不会退出。
这样,我们就不必拘泥于屏幕魔法,因为我们可以在 bash 范围内进行。
由于这种方法非常简单,它确实有一个缺陷,你会丢失你之前输入的变量赋值。但也许我们可以解决这个问题。无论如何,您都可以修改此代码以满足您的需要。
我对我在那里看到的任何其他解决方案都不满意,所以我最终解决了这个问题(添加到 .bashrc):
然后这个到.screenrc:
这与我通常做事的方式有点倒退。它取消设置别名并重新启用 ctrl-d -> 正常退出,但它检查 bash 是否立即在屏幕实例内运行(特别是它检查父进程的命令的开始是否是“屏幕”,然后是单词边界),然后它会禁用 ctrl-d/EOF 退出并使用退出和注销的提示别名设置退出。
我已经很久没有使用它了,但到目前为止我对它的工作方式非常满意。