如何找到特定 GNU 屏幕窗口中正在运行的进程?

如何找到特定 GNU 屏幕窗口中正在运行的进程?

问题

我需要找到屏幕上特定窗口上正在运行的进程(在合理的时间内)。

设想

我需要使用会话名称和窗口标题来查找其中运行的进程。它不需要太慢。

另外可能值得注意的是:我使用 byobu 作为屏幕的包装。

我尝试过的

  • 网上搜索
  • 阅读屏幕手册页(不适合胆小的人)。(好吧,我没有阅读全部内容,但我阅读了大部分相关部分,并非常彻底地搜索了任何可能有用的内容。)
    • 我学到的是:
      • 从 screen 获取我可能需要的信息(通过调用 screen)的唯一方法是使用它的命令行标志。
        • -Q 将允许您查询某些命令,但这些命令都没有提供我需要的一切。我正在使用的返回窗口号。
          • number - 我用来获取窗口号的
          • windowlist - 允许您获取自定义格式的信息字符串,但会话 PID 不是您可以要求的内容之一
          • echo、info、lastmsg、select、time、title 是其他的,这些看起来都没用
        • -ls 列出活动会话。它将 PID 添加到会话名称前面,因此这就是我当前获取会话 PID 的方式。
      • 一旦我获得了在特定窗口中运行的 shell 的 PID,我就可以检查其 WINDOW 的环境变量。该变量设置为窗口号。这就是我用来将进程与窗口相匹配的方法。
      • 没有一个命令可以让我返回会话 PID 以及窗口标题到窗口编号的映射。另外,我找不到任何方法可以确定性地找到呼叫屏幕之外的会话 ID 和窗口标题到窗口编号映射。
  • 试错/挖掘环境变量
  • 编写脚本

我的脚本

我编写了一个脚本,似乎成功解决了该问题,但运行时间略多于 0.75 秒。这对于我需要完成的工作来说太长了,但更重要的是,当服务器等待其完成以发送对 HTML 请求的响应时,这太长了。

这是脚本:

#!/bin/bash
# Accept the name of a GNU/screen session & window and return the process running in its shell
SessionName=$1
TabName=$2

# ====== Averages 0.370 seconds ======
# This finds the window number given the session name and window title
# The screen command queries screen for the window number of the specified 
# window title in the specified session.
# Example output of screen command: 1 (Main)
# Example output after cut command: 1
TargetTabNum=$(screen -S $SessionName -p $TabName -Q number | cut -d ' ' -f1)

# ====== Averages 0.370 seconds ======
# This finds the session PID given the session name.
# The screen command prints the list of session IDs
# Example output of screen command:
#     There is a screen on:
#             29676.byobu     (12/09/2019 10:23:19 AM)        (Attached)
#     1 Socket in /run/screen/S-{username here}.
# Example output after sed command: 29676
SessionPID=$(screen -ls | sed -n "s/\s*\([0-9]*\)\.$SessionName\t.*/\1/p")

# ====== The rest averages 0.025 seconds ======
# This gets all the processes that have the session as a parent,
# loops through them checking the WINDOW environment variable for
# each until it finds the one that matches the window title, and
# then finds the process with that process as a parent and prints its
# command and arguments (or null if there are no matching processes)
ProcessArray=( $(ps -o pid --ppid $SessionPID --no-headers) )
for i in "${ProcessArray[@]}"
do
    ProcTabNum=$(tr '\0' '\n' < /proc/$i/environ | grep ^WINDOW= | cut -d '=' -f2)
    if [ ! -z "$ProcTabNum" ] && [ "$TargetTabNum" -eq "$ProcTabNum" ]; then
        ProcInTab=$(ps -o cmd --ppid $i --no-headers)
        if [[ $? -eq 1 ]]; then
            ProcInTab=NULL
        fi
        echo $ProcInTab
        exit 0
    fi
done
echo "Couldn't find the specified Tab: $TabName" >&2
exit 1

如您所见,有问题的命令是两个屏幕命令。我可以通过搜索使用会话名称启动的屏幕进程来摆脱其中一个,但这感觉有点不稳定,我不确定它是否是确定性的:

SessionPID=$(ps -eo pid,command --no-headers | grep -i "[0-9]* screen.*\-s $SessionName " | grep -v grep | cut -d ' ' -f1)

目标

我希望有一种快速、可靠的方法来确定当前在特定屏幕窗口中运行的进程。我觉得我错过了一些东西,所以如果你们中有人发现它,我将非常感激!

(我对 StackExchange 还算了解,所以欢迎对我的问题提供任何反馈!)

答案1

从 screen 获取我可能需要的信息(通过调用 screen)的唯一方法是使用它的命令行标志。

不。你看错了screen方向。screen是一个虚拟伪终端(因此它挂钩/dev/pts/*),并且可以通过一百种不同的方式进行交互。当您运行时,screen您最终会看到一个可见的套接字,netstat您可以与之交互,并且您管理此套接字通信的方式将决定给定交换需要多长时间。尽可能快地进行这种通信的最佳方法是利用已经报告回来的内容,而不是发送请求并等待回复。

w对于您的情况,可以通过运行以下命令来解决该问题:

shark@tank:~$ sudo w
 20:39:11 up 2 days,  1:24,  2 users,  load average: 0.64, 0.41, 0.44
USER     TTY      FROM             LOGIN@   IDLE       WHAT
shark    tty7     :0               Sun19    2days      /usr/lib/xorg/Xorg :0 *
fish     pts/1    :pts/6:S.0       20:31    7:42       htop

正如您所看到的,第二个用户在连接到的套接字fish上进行操作,通过运行可以看出该套接字属于用户,并且该套接字正在侦听 /dev/pts/6 并且那里正在运行。screentty/dev/pts/1sharkls -la /dev/pts/htop

我们如何仔细检查这实际上是一个实际的套接字连接?通过运行:

sudo file /run/screen/*/*
/run/screen/S-fish/98633.pts-6.tank: socket

我们看到的数字对应于

ps aux | grep 98633 -B 1 -A 1 
fish      98632  0.0  0.0   6732  2964 pts/6   S+   20:31   0:00 screen
fish      98633  0.0  0.0   7032  2576 ?        Ss   20:31   0:00 SCREEN
fish      98634  0.0  0.0   8660  5400 pts/1   Ss+  20:31   0:00 /bin/bash

该命令从/proc和轮询此信息/var/run/utmp,并且命中率最小,可以通过运行看到:

$ sudo usr/bin/time -f "\t%E real\n\t%U user\n\t%S sys\n" w > /dev/null
    0:00.01 real
    0.00 user
    0.01 sys

相关内容