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