检查指定名称的屏幕是否存在

42

我已经制作了一个Bash文件,它启动另一个Bash文件并在独立的屏幕上使用唯一的名称,我需要确保任何时候只有一个内部的Bash文件正在运行。为了实现这一点,我希望父级Bash文件在尝试创建屏幕之前检查是否存在该名称的屏幕。是否有一种方法可以做到这一点?


如果你喜欢使用screen,不妨考虑尝试一下 tmux。虽然有很多不同之处,但切换到它是值得的。 - jpaugh
我主要使用screen,因为它具有类似守护进程的功能,缺乏更好的替代方案。如果tmux与CLI一样合作(例如允许通过bash将命令注入正在运行的screen),那么我可能会转换。 - Zoey
1
这个线程非常棒,非常适合我的使用情况...我一直在尝试使用pgrep和pkill,但是-Q和-X对于守护进程的检查非常好。我不同意所选答案...在这种情况下进行grep可能会有许多潜在的问题。 - Eyelash
可能是重复的问题,该问题已被问过:如何列出正在运行的Screen会话? - jww
绝对不是重复内容。 非常感谢您提出这个棒极了的问题。我学到了很多! 对于其他人:请查看我的评论,以确保检查其他用户的会话,因为当您需要确保其他人没有运行您的脚本时,这通常是必要的。 - Hicsy
6个回答

51

您可以使用screen -list命令的输出进行grep操作,以查找您要检查的会话名称:

if ! screen -list | grep -q "myscreen"; then
    # run bash script
fi

5
当然可以,但不使用grep的情况下如何检查呢?例如使用screen命令。假设你想知道是否存在会话“abcd”,但只有会话“abcdefgh”,那么你的条件会返回yes,但实际上会话不存在。 - e271p314
2
您可以修改 grep 命令。grep -q '^abcd$' 只会匹配完全符合的会话名称 'abcd'。 - chepner
4
似乎还是无法工作,因为“^abcd $”是匹配仅包含“abcd”的行的正则表达式。您的答案没问题。我的问题是,屏幕是否支持在具有特定名称的会话存在时返回0,否则不返回0的功能。 - e271p314
虽然我非常喜欢-Q select myWindow的想法(也检查特定窗口)...但这种grep方法是通过其他用户的会话进行最简单的检查:if (screen -ls user/ | grep -q '\.mySession\s'); - Hicsy
与检查脚本中指定屏幕的cron结合使用,效果完美。 - AxelS

22

您可以查询特定会话的屏幕“select”命令;如果会话存在,则shell结果为“0”,如果未找到命名的屏幕会话,则为“1”:

$ screen -S Tomcat
$ screen -S Tomcat -Q select . ; echo $?
0

相对于:

$ screen -S Jetty -Q select . ; echo $?
未找到屏幕会话。
1

请注意,在 select 后面的 '.' 是可选的,但可能更加健壮。


1
有没有关于“-Q”参数的文档?我的屏幕版本(Ubuntu 12)似乎没有它,这让我想知道这是否是非标准的或在不同版本中具有不同的别名。 - Namey
即使在Debian上的screen手册中没有提到,但它确实有效。 - mehulved
嗯,在Fedora 22上,使用screen 4.03.01,man页面确实提到了Q命令和可以查询的命令。我不确定在哪里找到了关于select语句中“.”的小提示... - troyfolger
对我来说,screen -S Tomcat -Q echo ''; echo $? 也可以工作,但不会触发重绘。 - Lars Schillingmann
在一个系统上(屏幕版本4.03.01)对我有效,而在另一个系统上(4.00.03)却无效。 - Digicrat
可以通过以下代码重定向噪音: if (screen -S user/session -Q select myWindow > /dev/null); .... 有趣的是: -Q 让你检查特定的窗口,而 -X select myWindow 总是像你写了 -X select . 一样行为,无论子窗口如何。另一个有趣的事情是:这些方法也固有地验证多用户会话上的权限,不像 -ls | grep - Hicsy

12

鉴于我不能评论,我将其作为新答案发布。troyfolger的回答是个好主意,基本上意味着尝试向会话发送一个几乎没有任何作用的命令。其中的一个问题是,在某些(较旧的)版本的screen上,-Q不受支持,因此对于这些版本,正确的命令是:

screen -S Jetty -X select . ; echo $?
将命令“select .”发送到名为“Jetty”的屏幕会话。
Select可以更改活动窗口,“.”表示当前活动窗口,因此尝试将活动窗口更改为当前活动窗口。只有当没有要连接的会话时才会失败,这正是我们想要的。
如果您阅读信息文档,则会建议仅使用select .作为测试或确保选择某些内容的唯一用途为-X。

嗯...我还是坚持使用-Q!: ^] 这可能是由于screen版本的差异。我认为旧版本的screen没有Q(查询)选项,因此您必须使用X选项(将命令发送到屏幕)。以下是screen 4.03.01的man页面中的相关信息:-Q 现在可以使用此标志从远程会话查询某些命令,例如“screen -Q windows”。命令将向查询进程的stdout发送响应。如果命令出现错误,则查询进程将以非零状态退出。 - troyfolger
我不会给你的回答点踩,但是你或许可以修改一下你的回答,改成“旧版本的 screen 需要使用 'X' 而不是 'Q' …” - troyfolger

4
所有提出的解决方案都没有处理那些没有唯一模式的屏幕名称,例如 "TEST" 和 "TEST123"。当你使用 screen -S "TEST" 或者 screen -list "TEST" 命令时,你可能会选择到 "TEST123" 屏幕!GNU screen 实现屏幕名称匹配存在某些问题(非确定性)。
下面是一个 bash 函数,它尝试进行精确匹配,并返回 PID.SCREEN NAME 以及一个退出代码:
function find_screen {
    if screen -ls "$1" | grep -o "^\s*[0-9]*\.$1[ "$'\t'"](" --color=NEVER -m 1 | grep -oh "[0-9]*\.$1" --color=NEVER -m 1 -q >/dev/null; then
        screen -ls "$1" | grep -o "^\s*[0-9]*\.$1[ "$'\t'"](" --color=NEVER -m 1 | grep -oh "[0-9]*\.$1" --color=NEVER -m 1 2>/dev/null
        return 0
    else
        echo "$1"
        return 1
    fi
}

使用方法 - 选择一个屏幕:

target_screen=$(find_screen "SCREEN NAME")
screen -S "$target_screen" ...etc...

用法 - 测试屏幕是否存在:

if find_screen "SCREEN NAME" >/dev/null; then
    echo "Found!"
fi

无论如何,这将涵盖99.9%的情况。为了99.99%的确定性,在屏幕名称中转义grep特殊字符。完全匹配需要grep匹配整行直到$,包括可能随版本变化的括号内日期。另一种完美匹配方法是:
ls -A -1 /var/run/screen/S-${USER} | grep "^[0-9]*\.SCREEN NAME$"

但这是一种不太优雅的方法,我们需要确保屏幕实现使用此文件夹。我不建议使用这种最后的方法。


1
一个更简单的解决方案:如果 screen -ls TEST | grep -q '.TEST\s',那么就输出 "TEST exists"。 - Ramast

3

更简单的方法是:

screen -xR -S SessionName

以下内容来自于此处Stéphane Chazelas评论:

我通常使用-xR进行附加或创建(如果没有要附加的内容)。

这样,您就不必搜索会话名称是否已存在,此方法将在会话存在时附加到会话中,如果不存在则会创建会话。


1

%100 工作。

screen -list | grep "SESSİON NAME" && echo "Active Program" || echo "Passive Program"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接