如何编程解析GNU Screen会话列表 兼容含括号的特殊会话名
PHP解析GNU Screen会话列表的实现方案
需求是通过编程方式解析GNU Screen的会话列表,开发语言为PHP,也可参考其他语言的实现逻辑。最初尝试使用正则表达式解析,基本功能接近可用,但无法正确处理会话名称包含括号的边缘场景(该场景为GNU Screen官方支持的特性)。
初始正则解析方案
代码实现
<?php declare(strict_types=1); function screen_list_sessions(): array { if (1) { // test code $raw = ' There are screens on: 2064.screen with parenthesis in name ( this is stupid ) (10/02/2021 12:07:22 PM) (Detached) 1996.test session with spaces (10/02/2021 12:00:35 PM) (Detached) 1985.testSessionWithDuplicateName (10/02/2021 12:00:23 PM) (Detached) 1974.testSessionWithDuplicateName (10/02/2021 12:00:16 PM) (Detached) 1963.testSessionWithDuplicateName (10/02/2021 12:00:09 PM) (Detached) 1871.testscreen (10/02/2021 11:59:23 AM) (Detached) 6 Sockets in /run/screen/S-root. '; } else { $raw = shell_exec('screen -list'); } $matches = []; $match_count = preg_match_all('/^\s+(?<session_id>\d+)\.(?<session_name>.*?)\((?<session_creation_date>[\s\S]+?)\)\s+\((?<session_state>[\s\S]+?)\)\s*?$/mu', $raw, $matches); $ret = []; for ($i = 0; $i < $match_count; ++$i) { $ret[] = [ "session_id" => $matches["session_id"][$i], "session_name" => $matches['session_name'][$i], "session_creation_date" => $matches["session_creation_date"][$i], "session_state" => $matches["session_state"][$i] ]; } return $ret; } var_export(screen_list_sessions());
运行输出
$ php test.php array ( 0 => array ( 'session_id' => '2064', 'session_name' => 'screen with parenthesis in name ', 'session_creation_date' => ' this is stupid ', 'session_state' => '10/02/2021 12:07:22 PM) (Detached', ), 1 => array ( 'session_id' => '1996', 'session_name' => 'test session with spaces ', 'session_creation_date' => '10/02/2021 12:00:35 PM', 'session_state' => 'Detached', ), 2 => array ( 'session_id' => '1985', 'session_name' => 'testSessionWithDuplicateName ', 'session_creation_date' => '10/02/2021 12:00:23 PM', 'session_state' => 'Detached', ), 3 => array ( 'session_id' => '1974', 'session_name' => 'testSessionWithDuplicateName ', 'session_creation_date' => '10/02/2021 12:00:16 PM', 'session_state' => 'Detached', ), 4 => array ( 'session_id' => '1963', 'session_name' => 'testSessionWithDuplicateName ', 'session_creation_date' => '10/02/2021 12:00:09 PM', 'session_state' => 'Detached', ), 5 => array ( 'session_id' => '1871', 'session_name' => 'testscreen ', 'session_creation_date' => '10/02/2021 11:59:23 AM', 'session_state' => 'Detached', ), )
存在问题
第一条名称带括号的会话解析完全错位:名称中括号内的内容被错误识别为创建日期,状态字段混入了创建日期和多余的格式字符,不符合预期。
优化后的非正则解析方案
代码实现
<?php declare(strict_types=1); function screen_list_sessions(): array { if (1) { // test code $raw = ' There are screens on: 2064.screen with parenthesis in name ( this is stupid ) (10/02/2021 12:07:22 PM) (Detached) 1996.test session with spaces (10/02/2021 12:00:35 PM) (Detached) 1985.testSessionWithDuplicateName (10/02/2021 12:00:23 PM) (Detached) 1974.testSessionWithDuplicateName (10/02/2021 12:00:16 PM) (Detached) 1963.testSessionWithDuplicateName (10/02/2021 12:00:09 PM) (Detached) 1871.testscreen (10/02/2021 11:59:23 AM) (Detached) 6 Sockets in /run/screen/S-root. '; } else { $raw = shell_exec('screen -list'); } $raw = implode("\n", array_filter(explode("\n", $raw), function (string $line): bool { // remove uninteresting lines; only lines that start with spaces are interesting.. $trimmed = ltrim($line); if ($trimmed === $line) { return false; } if (strlen($trimmed) < 1) { // empty lines are also uninteresting return false; } // interesting line return true; })); $ret = []; $lines = explode("\n", $raw); foreach ($lines as $key => $line) { $current = []; $lastParStart = strrpos($line, '('); if ($lastParStart === false) { throw new \LogicException("line without \"(\": {$line}"); } $lastParEnd = strrpos($line, ')'); if ($lastParEnd === false) { throw new \LogicException("line without \")\": {$line}"); } $current['session_state'] = substr($line, $lastParStart + strlen('('), $lastParEnd - $lastParStart - strlen(')')); $line = substr($line, 0, $lastParStart); $lastParStart = strrpos($line, '('); if ($lastParStart === false) { throw new \LogicException("line without 2nd \"(\": {$line}"); } $lastParEnd = strrpos($line, ')'); if ($lastParEnd === false) { throw new \LogicException("line without 2nd \")\": {$line}"); } $current['session_creation_date'] = substr($line, $lastParStart + strlen('('), $lastParEnd - $lastParStart - strlen(')')); $line = substr($line, 0, $lastParStart - strlen("\t")); $dotPos = strpos($line, '.'); if (false === $dotPos) { throw new \LogicException(""); } $current['session_id'] = trim(substr($line, 0, $dotPos)); // using trim() here probably means we won't 100% support sessions whose names starts or ends with space... $current['session_name'] = trim(substr($line, $dotPos + strlen("."))); // just put keys in pretty-ish order $current = [ "session_id" => $current['session_id'], "session_name" => $current['session_name'], "session_state" => $current['session_state'], "session_creation_date" => $current['session_creation_date'], ]; $ret[] = $current; } return $ret; } var_export(screen_list_sessions());
运行输出
array ( 0 => array ( 'session_id' => '2064', 'session_name' => 'screen with parenthesis in name ( this is stupid )', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 12:07:22 PM', ), 1 => array ( 'session_id' => '1996', 'session_name' => 'test session with spaces', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 12:00:35 PM', ), 2 => array ( 'session_id' => '1985', 'session_name' => 'testSessionWithDuplicateName', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 12:00:23 PM', ), 3 => array ( 'session_id' => '1974', 'session_name' => 'testSessionWithDuplicateName', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 12:00:16 PM', ), 4 => array ( 'session_id' => '1963', 'session_name' => 'testSessionWithDuplicateName', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 12:00:09 PM', ), 5 => array ( 'session_id' => '1871', 'session_name' => 'testscreen', 'session_state' => 'Detached', 'session_creation_date' => '10/02/2021 11:59:23 AM', ), )
方案说明
该方案从右往左匹配括号提取状态和创建日期,规避了名称中括号的干扰,输出结果完全符合预期,可正确解析名称包含括号的会话。
当前存在的局限:由于会话名称提取时使用了trim()处理,暂时不支持解析名称首尾带空格的Screen会话。
内容的提问来源于stack exchange,提问作者hanshenrik
相关产品推荐
相关产品推荐

