将字符串解析为命令行的数组输出

时间:2015-06-21 10:14:51

标签: php regex bash symfony docker

我正在开发一个新的Symfony 2项目,该项目将成为Docker容器的面板管理。

在这个项目中,我正在使用exec() PHP函数执行一些命令。

我正在尝试解析以下命令的输出:

docker create tutum/lamp:latest --name test 2>&1

当命令成功时,我将容器ID放在一个好的,易于使用的字符串中,但是当出现问题时,它不一样。结果是一个带有var =“data”语法的字符串,我想解析它以获得一个数组。

命令输出:

time="2015-06-21T11:33:26+02:00" level="fatal" msg="Error response from daemon: Conflict. The name \"test\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name."

我希望有类似的东西:

Array( time => "2015-06-21T11:33:26+02:00", level => "fatal" ...);

我知道我必须进行正则表达式解析。过了一会儿(正则表达式和我不是最好的朋友)我得到这个正则表达式(在https://regex101.com/上测试):

/([a-zA-Z]+)="((.*)*)"/

我使用preg_split函数我不确定它是好的。

preg_split('/([a-zA-Z]+)="((.*)*)"/', $output)

结果是:

array(2) { [0]=> string(0) "" [1]=> string(0) "" }

你有什么建议可以帮助我吗? 非常感谢你的帮助。

2 个答案:

答案 0 :(得分:1)

TL; DR:这应该有效:

preg_match_all(',([a-z]+)="((?:[^"]|\\\\")*[^\\\\])",', $a, $matches, PREG_SET_ORDER);
var_dump($matches);

最后var_dump打印以下数据结构,应该易于处理:

array(3) {
  [0] => array(3) {
    [0] => string(32) "time="2015-06-21T11:33:26+02:00""
    [1] => string(4) "time"
    [2] => string(25) "2015-06-21T11:33:26+02:00"
  }
  [1] => array(3) {
    [0] => string(13) "level="fatal""
    [1] => string(5) "level"
    [2] => string(5) "fatal"
  }
  [2] => array(3) {
    [0] => string(179) "msg="Error response from daemon: Conflict. The name \\"test\\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name.""
    [1] => string(3) "msg"
    [2] => string(173) "Error response from daemon: Conflict. The name \\"test\\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name."
  }
}

为什么会这样?

正则表达式解释说:

([a-z]+)                    # Match the label ("time", "level" or "msg")
=                           # Self-explanatory
"((?:[^"]|\\\\")*[^\\\\])"  # This is the tricky part:
                            # Match the quoted string; this is a sequence
                            # of (a) non-quote characters ([^"]) or
                            # (b) escaped quote characters (\\\\").

其他一些说明:

  1. preg_split使用正则表达式来匹配应该拆分字符串的标记。在这种情况下,这不是你想要的;您希望通过正则表达式返回匹配的字符串部分。为此,您应该使用preg_match(或者,如果您希望模式多次匹配),preg_match_all
  2. 还要考虑PREG_SET_ORDER的{​​{1}}标记。此标志使preg_match_all结果从输出消息中为每个标签包含一行,这使得数据结构易于处理。试着看看如果你把它拿出来会发生什么。

答案 1 :(得分:1)

这是因为greedy dot会将你的字符串吃掉到最后"。让它变得懒惰,会这样做:

if(preg_match_all('/(\w+)="(.*?)(?<!\\\)"/s', $str, $out))
  print_r(array_combine($out[1], $out[2]));

\w[a-zA-Z0-9_]的{​​{3}}。使用后备(?<!\\\)来逃脱报价(short)。

使用s标志使点匹配换行符。 see regex101输出到:

  

阵   (       [时间] =&gt; 2015-06-21T11:33:26 + 02:00       [level] =&gt;致命       [msg] =&gt;来自守护程序的错误响应:冲突。名称\&#34; test \&#34;已被容器XXXXXXXX使用。您必须删除(或重命名)该容器才能重用该名称。   )