将字符串解析为命令行的数组输出
I'm working on a new Symfony 2 project which will be a panel management for Docker containers.
In this project, I'm executing some commands with the exec()
PHP function.
I'm trying to parse the output of the following command :
docker create tutum/lamp:latest --name test 2>&1
When the command is a success I'm getting the container ID in a string which is good and easy to use but when a problem occured it's not the same. The result is a string with a var="data" syntax which I want to parse in order to get an array.
The command output :
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."
I wish to have something like that :
Array( time => "2015-06-21T11:33:26+02:00", level => "fatal" ...);
I know that I have to do a regex parsing. After a while (regex and me are not realy best friends) I get this regex (tested on https://regex101.com/) :
/([a-zA-Z]+)="((.*)*)"/
I used preg_split function i'm not sure that it's the good one.
preg_split('/([a-zA-Z]+)="((.*)*)"/', $output)
Result is :
array(2) { [0]=> string(0) "" [1]=> string(0) "" }
Have you any suggestions to help me ? Many thanks for your help.
我正在开发一个新的Symfony 2项目,该项目将成为Docker容器的面板管理。 p>
在这个项目中,我正在使用 我是 尝试解析以下命令的输出: p>
当命令成功时,我将容器ID放在一个好的,易于使用的字符串中,但是当出现问题时,它不一样。 结果是一个带有var =“data”语法的字符串,我想解析它以获得一个数组。 p>
命令输出: p>
我希望有类似的东西: p> \ n
我知道我必须进行正则表达式解析。 过了一会儿(正则表达式和我不是最好的朋友)我得到这个正则表达式(在 https://regex101.com/上测试 a>): p>
我使用preg_split函数我不确定它是好的。 p>
结果是: p>
您有任何建议可以帮助我吗?
非常感谢您的帮助。 p>
div> exec() code> PHP函数执行一些命令。 p>
docker create tutum / lamp:latest --name test 2>& 1
code> pre>
time =“2015-06-21T11:33:26 + 02:00”level =“fatal”msg =“来自守护程序的错误响应:冲突。容器XXXXXXXX已在使用名称\”test \“ 你必须删除(或重命名)该容器才能重用该名称。“
code> pre>
Array(time =>“2015-06-21T11:33:26 + 02:00”,level =>“致命”......);
code>
/([a-zA-Z] +)=“((。*)*)”/
code> pre>
preg_split('/([a-zA-Z] +)=“((。*)*)”/',$ output)
code> pre>
array(2){[0] => string(0)“”[1] => string(0)“”}
code> pre>
TL;DR: This should work:
preg_match_all(',([a-z]+)="((?:[^"]|\\\\")*[^\\\\])",', $a, $matches, PREG_SET_ORDER);
var_dump($matches);
The last var_dump
prints the following data structure, which should be easy to process:
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."
}
}
Why this works
The regular expression explained:
([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 (\\\\").
Some other notes:
-
preg_split
uses the regular expression to match token at which the string should be split. That's not what you want in this case; you want to return the parts of the string that was matched by the regular expression. For this, you should usepreg_match
(or if, like here, you want a pattern to match multiple times),preg_match_all
. - Also consider the
PREG_SET_ORDER
flag forpreg_match_all
. This flag causes the$matches
result to contain one row for each label from output message, which makes the data structure easy to process. Try and see what happens if you leave it out.
It's because of the greedy dot that eats up your string to the last "
. Make it lazy, would do like that:
if(preg_match_all('/(\w+)="(.*?)(?<!\\\)"/s', $str, $out))
print_r(array_combine($out[1], $out[2]));
\w
is a short for [a-zA-Z0-9_]
. The lookbehind (?<!\\\)
to eat up escaped quotes (see regex101).
Used s
flag for making the dot match newline. Test at eval.in, outputs to:
Array ( [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. )