前言

大一的时候给 IKnowISee多功能AR眼镜 项目写了一个脚本,主要的要求是:1、一个脚本启动多个 python 文件;2、保持这几个 python 进程在前台运行,因为要随时查看控制台输出

当时我使用了 screen 来保留 python 进程的输出,脚本内容如下

#!/bin/bash

echo "项目准备中..."
do_in_screen(){
        screen -x -S ${1} -p 0 -X stuff "${2}"
        screen -x -S ${1} -p 0 -X stuff $"\n"
}
launch(){
    screen -dmS ${1}
    do_in_screen ${1} "source activate"
    do_in_screen ${1} "conda activate iknowisee"
    do_in_screen ${1} "python ${2}"
    echo "已启动 ${1}.py"
}

# 设置文件路径
file1="/root/python/Dlib_face_recognition_from_camera-master/main.py"
file2="/root/python/Dlib_face_recognition_from_camera-master/newtest.py"
file3="/root/python/Dlib_face_recognition_from_camera-master/newtest1.py"
file4="/root/python/shuaxin.py"
file5="/root/python/shuaxin-bei.py"
file6="/root/python/openai.py"
echo "关键路径设置成功"

# 运行Python文件
launch "main" "$file1"
launch "newtest" "$file2"
launch "newtest1" "$file3"
launch "shuaxin" "$file4"
launch "shuaxin-bei" "$file5"

echo "完成!"
echo "创建了以下终端"
screen -ls

当时写的时候大量借助了 AI,知其然而不知其所以然,最近又想用 screen 写点小工具,所以认真学习一下这个命令的使用方法

什么是 screen

screen 命令是一个用于管理 Linux 窗口的 bash 程序,使用 screen 命令,你可以启动多个屏幕会话。屏幕会话也可以有多个窗口

screen 命令通过将终端上运行的进程放在不同的会话中来帮助分离它们,screen 命令允许你在会话之间切换并选择要附加的会话

screen 最有用的功能当属其会话恢复功能,只要Screen本身没有终止,在其内部运行的会话都可以恢复

在很多 Linux 发行版中都默认搭载了 screen,所以在编写脚本时常能直接拿来使用,即使默认没有安装 screen,也可以通过 apt 或者 yum 等软件源来安装

# CentOS
yum install screen
# Debian/Ubuntu
apt install screen

screen 常见用法

创建/退出会话

最简单的用法:创建并进入一个自命名会话

screen -S my_session

在 screen 会话中时,使用 Ctrl-A D 可以在保持会话在后台运行的情况下退出会话

也可以使用下面的命令退出

screen -d

查看会话

我们可以使用 -ls 来查看所有的 screen 会话

screen -ls

输出会类似这样:

# screen -ls
There is a screen on:
        599.my_session  (09/04/24 22:50:29)     (Detached)
1 Socket in /run/screen/S-root.

使用 screen ls [key_word] 可以查看包含指定关键字的会话

# screen -ls session
There is a screen on:
        599.my_session  (09/04/24 22:50:29)     (Detached)
1 Socket in /run/screen/S-root.

进入会话

想要重新进入已经退出的会话,可以使用

screen -r [session]

这里的 [session] 可以是会话的名称,也可以是会话的 id,下面的三种用法都是可以的

screen -r 599
screen -r my_session
screen -r 599.my_session

但是使用 -r 有时候会报错:

# screen -r my_session
There is a screen on:
        599.my_session (09/05/24 00:52:53)     (Attached)
There is no screen to be resumed matching my_session.

这是因为要进入的会话的状态是 Attached(附加状态,表示该screen正在作为主终端使用)

当有用户进入了某个会话时,那个 session 就会标记为 Attached,使用 screen -d 退出后又会重新回到 Detached(分离状态,表示该screen正在后台使用)

此时可以使用下面的命令来进入处于附加状态的会话

screen -x my_session

该命令对于 Detached session 的效果和 -r 相同,对于 Attached session 则会在不影响原有使用者的情况下进入会话,此时你在 session 中进行的所有操作,会同步展示给原本的使用者,其他人的操作也一样,相当于”多人同时在一个屏幕中工作“

删除会话

可以在会话中使用 exit 来退出会话,这种退出方式会同时删除 session

screen -r my_session # 进入会话
exit                 # 退出

也可以使用下面的命令

screen -S my_session -X quit
-S 指定了要操作的 session 名称
-X 的作用是发送命令至会话, 这里发送了quit命令, 也就是关闭会话的命令

窗口操作

screen 可以在一个会话中创建多个窗口,并在窗口之间切换

稍微不好的一点:窗口操作只能用组合键来完成,对脚本不太友好

  • Ctrl-a c:创建一个新的窗口,并切换到该窗口
  • Ctrl-a p/n:切换到 上一个/下一个 窗口
  • Ctrl-a [0-9]:切换到指定编号的窗口
  • Ctrl-a q:关闭当前窗口
  • Ctrl-a ":列出所有窗口,并显示它们的编号

一些对脚本友好的用法

创建具名会话但不进入

screen -dmS my_session

向指定会话发送命令

screen -x -S [session] -p 0 -X stuff "[command]\n"

-x:仅在需要附加到一个已经存在的、并且已经分离的 screen 会话时使用

-S [session]:用于指定会话的名称,如果会话已经存在并且你想要附加到它,这是必要的

-p 0:指定要发送命令的窗口索引,0 表示第一个窗口

-X stuff “[command]\n”:向 screen 会话发送指定的命令,\n模仿键盘回车进行命令执行

销毁一个正在运行的会话

PID=`screen -ls | grep -w [session] | awk '{print $1}' | cut -d '.' -f1`
kill -9 $PID
screen -wip

使用 grep + awk + cut 获取指定名称的 session 的 pid,然后直接 kill 掉进程,最后使用 wip 销毁被 kill 的会话