正则表达式从命令历史记录中获取执行的脚本名称

时间:2011-05-25 16:50:30

标签: regex perl

我正在尝试编写一个正则表达式来解析调用脚本的语法,捕获脚本名称

所有这些都是调用的有效语法

# normal way
run cred=username/password script.bi

# single quoted username password, also separated in a different way
run cred='username password' script.bi

# username/password is optional
run script.bi

# script extension is optional
run script

# the call might be broken into multiple lines using \
# THIS ONE SHOULD NOT MATCH
run cred=username/password \
script.bi

这是我到目前为止所拥有的

my $r = q{run +(?:cred=(?:[^\s\']*|\'.*\') +)?([^\s\\]+)};

捕获$1中的值。

但我得到了

Unmatched [ before HERE mark in regex m/run +(?:cred=(?:[^\s\']*|\'.*\') +)?([ << HERE ^\s\]+)/

3 个答案:

答案 0 :(得分:3)

\\被视为\,因此在正则表达式中它变为\],因此转移],因此无法匹配[

替换为run +(?:cred=(?:[^\s\']*|\'.*\') +)?([^\s\\\\]+)(请注意\\\\)并尝试。

此外,根据评论,您必须使用qr作为正则表达式,而不仅仅是q

(我刚刚查看了错误,而不是正则表达式对你的问题的有效性/效率)

答案 1 :(得分:3)

指定正则表达式时问题的本质是一个字节的差异:qqr。你正在写一个正则表达式,所以称之为它是什么。将模式作为字符串处理意味着您必须处理字符串引用的规则正则表达式转义的规则。

对于正则表达式匹配的语言,添加锚点以强制模式匹配整行。正则表达式引擎是非常坚定的,并将继续工作,直到找到匹配。没有锚点,很高兴找到一个子串。

有时这会给你带来惊人的效果。你曾经和一个脾气暴躁的孩子(或一个幼稚的成年人)打过交道,他们对你所说的话进行了狭隘的,非常直译的解释吗?正则表达式引擎就是这样,但它正试图提供帮助。

最后一个例子匹配,因为

  • 你用?量词表示cred=...子模式可以匹配零次,所以正则表达式引擎跳过它。
  • 您说脚本名称是以下子字符串,它是一个或多个非空格,非反斜杠字符的运行,因此正则表达式引擎看到cred=username/password,其中没有一个是空格或反斜杠字符,并且匹配。正则表达式是贪婪的:他们认为在他们面前是正确的,而不考虑给定的子字符串“应该”是否与另一个子模式匹配。

最后一个例子符合法案 - 尽管不是你想要的方式。正则表达式的一个重要教训是any quantifier such as ? or * that can match zero times always succeeds!

如果没有$锚点,问题中的模式会留下不匹配的尾部反斜杠,只需稍加修改$runpat即可看到。

qr{run +(?:cred=(?:[^\s']*|\'.*\') +)?([^\s\\]+)(.*)}; # ' SO hiliter hack

注意最后的(.*)抓取可能留下的任何非换行符。将循环更改为

while (<DATA>) {
  next unless /$runpat/;
  print "line $.: \$1=[$1]; \$2=[$2]\n";
}

为第15行提供以下输出。

line 15: $1=[cred=username/password]; $2=[ \]

作为一个完整的程序,即成为

#! /usr/bin/env perl

use strict;
use warnings;

# The goofy comment on the next line is a hack to
# help Stack Overflow's syntax highlighter recover
# from its confusion after seeing the quotes. It's
# for presentation only: you won't need it in your
# real code.
my $runpat = qr{^\s*run +(?:cred=(?:[^\s']*|\'.*\') +)?([^\s\\]+)$}; # '

while (<DATA>) {
  next unless /$runpat/;
  print "line $.: \$1=[$1]\n";
}

__DATA__
# normal way
run cred=username/password script.bi

# single quoted username password, also separated in a different way
run cred='username password' script.bi

# username/password is optional
run script.bi

# script extension is optional
run script

# the call might be broken into multiple lines using \
# THIS ONE SHOULD NOT MATCH
run cred=username/password \
script.bi

输出:

line 2: $1=[script.bi]
line 5: $1=[script.bi]
line 8: $1=[script.bi]
line 11: $1=[script]

简洁并不总是有助于正则表达式。考虑以下备选但等效的规范:

my $runpat = qr{
  ^ \s*
  (?:
    run \s+ cred=(?:[^\s']*|'.*?') \s+ (?<script> [^\s\\]+)  # ' hiliter
  | run \s+ (?!cred=)                  (?<script> [^\s\\]+)
  )
  \s* $
}x;

是的,它需要更多的空间来写,但是关于可接受的替代方案更清楚。你的循环几乎相同

while (<DATA>) {
  next unless /$runpat/;
  print "line $.: script=[$+{script}]\n";
}

甚至让那些可怜的读者不必计算括号。

要使用命名捕获缓冲区,例如(?<script>...),请务必添加

use 5.10.0;

到程序的顶部,提供perl所需最低版本的可执行文档。

答案 2 :(得分:0)

脚本有时会出现争论吗?如果没有,为什么不:

/^run(?:\s.*\s|\s)(\S+)\s*$/

我想这对行继续位不起作用。

/^run(?:\s+cred=(?:[^'\s]*|'[^']*')\s+|\s+)([^\\\s]+)\s*$/

测试程序:

#!/usr/bin/perl

$foo="# normal way
run cred=username/password script.bi

# single quoted username password, also separated in a different way
run cred='username password' script.bi

# username/password is optional
run script.bi

# script extension is optional
run script

# the call might be broken into multiple lines using \
# THIS ONE SHOULD NOT MATCH
run cred=username/password \\
script.bi
";

foreach my $line (split(/\n/,$foo))
{
  print "Looking >$line<\n";
  print "Match >$1<\n"
    if ($line =~ /^run(?:\s+cred=(?:[^'\s]*|'[^']*')\s+|\s+)([^\\\s]+)\s*$/);
}

示例输出:

Looking ># normal way<
Looking >run cred=username/password script.bi<
Match >script.bi<
Looking ><
Looking ># single quoted username password, also separated in a different way<
Looking >run cred='username password' script.bi<
Match >script.bi<
Looking ><
Looking ># username/password is optional<
Looking >run script.bi<
Match >script.bi<
Looking ><
Looking ># script extension is optional<
Looking >run script<
Match >script<
Looking ><
Looking ># the call might be broken into multiple lines using <
Looking ># THIS ONE SHOULD NOT MATCH<
Looking >run cred=username/password \<
Looking >script.bi<