MAC 格式 xxxx.xxxx.xxxx 的批量正则表达式

Regex in Batch for MAC of format xxxx.xxxx.xxxx

我一直在尝试解析格式为 "xxxx[Delimiter]xxxx[Delimiter]xxxx" 的 mac,其中 "x" 在 [0-9,A-F,a-f] 中,并且“[定界符]”在[:,.,-]中。我尝试了以下代码,并引用了标题为 Regex to match a variable in Batch scripting and 的示例。

set MACAddr=012a.23fa.5ffc
If [%MACAddr%] EQU [] (
echo MAC Address is not set. Please set it to proceed.
) else (
echo %MACAddr%|findstr /r "^([0-9A-Fa-f]{2}[:.-]?){5}([0-9A-Fa-f]{2})$"
if errorlevel 1 (echo Mac %MACAddr% is not of same Format as xxxx.xxxx.xxxx) else ( pause )
)

也试过这个

echo %MACAddr%|findstr /r "^([0-9A-Fa-f]{4})([:.-])([0-9A-Fa-f]{4})([:.-])([0-9A-Fa-f]{4})$"

但它只能运行if errorlevel 1 (echo Mac %MACAddr% is not of same Format as xxxx.xxxx.xxxx)。请告诉我我做错了什么。

findstr 具有非常有限的 REGEX 子集(参见 for /?

您的要求可以表述为:

findstr /ri "^[0-9A-F][0-9A-F][0-9A-F][0-9A-F].[0-9A-F][0-9A-F][0-9A-F][0-9A-F].[0-9A-F][0-9A-F][0-9A-F][0-9A-F]$"

其中 . 表示 "any char"。如果您想将其分解为 ;.- 作为分隔符,请使用 [;.-] 而不是 .。如果分隔符可能存在或可能不存在,请改用 [;.-]*(其中 * 表示 "zero or more" - 抱歉,没有 ? "none or one" 和 findstr.)

你知道当你遇到这样的问题时,你是如何继续前进直到找到解决方案的吗?

@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION 

:: This simply tests various addresses

FOR %%D IN (":" "." "-" "#") DO FOR %%T IN (
.012a.23fa.5ffc
012a.23fa.5ffc.
012a.23fa.5ffc
012a.23fa
012a.23fa.5ffc.0123
01ka.23fa.5ffc
012a.23ga.5ffc
012a.23fa.5pfc
012a1.23fa.5ffc
012a.263fa.5ffc
012a.23fa.58ffc
1.2.3
03ed.2.f
) DO (
 set "MACAddr=%%T"
 SET "macaddr=!macaddr:.=%%~D!"
 CALL :testmacaddr
)
SET "macaddr="
CALL :testmacaddr

GOTO :eof

:testmacaddr
SET "invalid="
SET "valid="
If NOT DEFINED MACAddr SET "invalid=MACADDR not set"&GOTO report
FOR %%d IN (":" "." "-") DO (
 IF "%macaddr:~0,1%"=="%%~d" SET "invalid=Initial"
 IF "%macaddr:~-1%"=="%%~d" SET "invalid=Terminal"
 SET "control="tokens=1-4delims=%%~d""
 CALL :tokenise
)
:report
IF NOT DEFINED valid IF NOT DEFINED invalid SET "invalid=Incorrect octet count"
:: pad 'macaddr' with a goodly number of spaces,
:: show the first 20 character of the result and the 'invalid' value

SET "macaddr=%macaddr%                          "
ECHO %macaddr:~0,20% : %invalid%
GOTO :EOF

:tokenise
FOR /f %control% %%p IN ("%macaddr%") DO (
 IF "%%s"=="" IF "%%r" neq "" (
  SET /a octets=1
  CALL :validate %%p %%q %%r
 )
)

GOTO :eof

:validate
ECHO %1|FINDSTR /r /x /i "[0-9a-f]">NUL
IF NOT ERRORLEVEL 1 GOTO valok
ECHO %1|FINDSTR /r /x /i "[0-9a-f][0-9a-f]">NUL
IF NOT ERRORLEVEL 1 GOTO valok
ECHO %1|FINDSTR /r /x /i "[0-9a-f][0-9a-f][0-9a-f]">NUL
IF NOT ERRORLEVEL 1 GOTO valok
ECHO %1|FINDSTR /r /x /i "[0-9a-f][0-9a-f][0-9a-f][0-9a-f]">NUL
IF ERRORLEVEL 1 SET "invalid=Octet %octets%"&GOTO :eof
:valok
shift
SET /a octets+=1
IF %octets% lss 4 GOTO validate
SET "valid=Y"

GOTO :eof

所以 - 第一部分只是生成一系列 "possible" 条目用于测试。将 macaddr 分配给测试字符串的例程 :testmacaddr 实际上完成了工作,标签 :report 之后的部分报告结果 - 如果定义了 invalid,则提供的 macaddr 无效,原因在 %invalid%

for %%d 循环检查初始或终止定界符并为 :tokenise 例程中的内部循环建立控制字符串。

:tokenise 例程使用当前定界符检查提供的前 4 个标记的地址,将八位字节分配给 %%p..%%s。由于我们正好需要 3,因此 %%s 必须为空并且 %%r 非空以需要使用 :validate

进一步验证

:validate 检查每个八位组是否 /x/r 正则表达式 1、2、3 或 4 个十六进制数字不区分大小写的 /i 完全匹配。如果八位字节未通过所有这些测试,则 invalid 设置为失败的八位字节编号。如果所有八位字节都通过,则 valid 设置为 Y

因此,如果没有失败条件并且 :validate 例程已通过一次,则该值有效。

由于 Stephan already mentioned in findstr 没有完整的 RegEx 支持。

所以分组 ((/))、备选 (|)、重复 ({/}) 和选项 (?) 不受支持。

还有一个额外的限制需要考虑:字符数类([/])限制为15个,所以指定16个或更多导致错误。
还不够,字符 类 有问题,它们可能会匹配意想不到的字符;例如,范围 [0-9] 匹配十进制数字 09,但也匹配字符 ²³;范围 [A-Z](没有 /I 选项)也错误地匹配小写字母,如 z,以及一些其他字符,如 Áá , 取决于当前代码页。

参考伟大的 post by dbenham 关于 findstr 的所有错误和限制。


要解决所有这些问题,您可以指定多个搜索字符串,例如 findstr /R "search_string1 search_string2"(白色-space-分隔列表)或 findstr /R /C:"search string1" /C:"search string2"(这甚至允许白色-spaces 将成为搜索字符串的一部分)。

我会这样完成你的任务:

echo %MACAddr%| (findstr /I /X /R ^
    /C:"[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F]" ^
    /C:"[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F]" ^
    /C:"[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F]" ^
    /C:"[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]" ^
    /C:"[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]" ^
    /C:"[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]" ^

)

不用/C的语法也可以,但是读起来很糟糕:

echo %MACAddr%| findstr /I /X /R "[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F] [0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F] [0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F] [0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F] [0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F] [0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]"

您可以将搜索字符串放入文本文件(例如 MACAddrRegEx.txt)并使用此语法:

echo %MACAddr%| findstr /I /X /R /G:"MACAddrRegEx.txt"

MACAddrRegEx.txt的内容是:

[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F][0-9A-F][0-9A-F]
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:[0-9A-F][0-9A-F][0-9A-F][0-9A-F]
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-[0-9A-F][0-9A-F][0-9A-F][0-9A-F]
[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]\.[0-9A-F][0-9A-F]
[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]
[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]-[0-9A-F][0-9A-F]

如果您不想接受以四个为一组的十六进制数字字符串,请删除前三个搜索字符串;如果您不想接受两个一组的字符串,请删除后三个。

前面提到的关于字母范围大小写匹配的错误在这里不适用,因为指定了 /I 选项,它定义了不区分大小写的搜索。然而,其他与范围相关的错误在这里被忽略,否则搜索字符串变得非常长(请注意,[0-9] 需要更改为 [0123456789] 以便仅匹配十进制数字;字母相同)因此将超过长度限制。