2012-09-23 45 views
5

Debo verificar la validez de una cadena almacenada en una variable, no puedo usar las utilidades externas de la CLI (grep, awk, etc.) así que elegí FINDSTR. La cadena tiene el siguiente formato (en la expresión regular):Usar subpatterns en FINDSTR

([1-9][0-9]*:".*"(|".*")*) 

no sé cómo comprobar el sub-patrón (| "*".). Actualmente mi código es:

((ECHO.) | (SET /P "=(11:"a"|"b"|"c")") | (FINDSTR /R /C:"^([1-9][0-9]*:".*")$")) 

Saludos.

+2

Si es posible, probablemente sea mejor que use vbscript o powershell. La manipulación de cadenas que contienen caracteres especiales es absurdamente difícil en los archivos por lotes de Windows. –

+0

@Harry Johnston Desafortunadamente, no puedo usar nada más que comandos estándar internos o externos para cmd.exe. – networkcode

+1

VBScript y JScript son utilidades nativas estándar disponibles para CMD.EXE, con buena compatibilidad con expresiones regulares. PowerShell es nativo desde Vista en adelante, y también tiene buen soporte de expresiones regulares. – dbenham

Respuesta

6

Mat M es correcta acerca de la limitación de FINDSTR. El soporte de expresiones regulares FINDSTR es muy primitivo y no estándar. Escriba HELP FINDSTR o FINDSTR /? desde la línea de comandos para obtener una breve sinopsis de lo que es compatible. Para una explicación detallada, refiérase a What are the undocumented features and limitations of the Windows FINDSTR command?

Me gusta el comentario de Harry Johnston - Sería bastante fácil crear una solución usando VBScript o JavaScript. Creo que sería una opción mucho mejor.

Pero, aquí hay una solución nativa por lotes. Incorporé la regla adicional sobre la cantidad de subpatrones que el OP indicó en el comentario a la respuesta de Mat M.

La solución es sorprendentemente difícil. Los caracteres especiales pueden causar problemas al conectar la salida de ECHO a FINDSTR debido a la forma en que funcionan las tuberías. Cada lado de la tubería se ejecuta en su propia sesión de CMD. Los caracteres especiales deben ser citados, escapados dos veces o solo expuestos a través de una expansión demorada. Elegí usar la expansión retrasada, pero los caracteres ! deben escaparse dos veces para asegurarse de que la expansión retrasada ocurra en el momento correcto.

La manera más fácil de analizar un número variable de subpatrones es reemplazar el delimitador por una nueva línea y usar FOR/F para iterar cada subpatrón.

La mitad superior de mi código es un arnés de codificación frágil para iterar y probar convenientemente un conjunto de cadenas. No funcionará correctamente con ninguno de <space>;,=<tab>* o ? en la cadena. Además, las comillas deben estar balanceadas en cada cadena.

Pero la rutina de validación más importante puede manejar cualquier cadena en la variable var.

@echo off 
setlocal 
set LF=^ 


::Above 2 blank lines are critical for creating a linefeed variable. Do not remove 

set test=a 

for %%S in (
    "(3:"a"|"c"|"c")" 
    "(11:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k")" 
    "(4:"a"|"b"|"c")" 
    "(10:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k")" 
    "(3:"a"|"b"|"c"" 
    "(3:"a"|"b^|c")" 
    "(3:"a"|"b"|c)" 
    "(3:"a"|"b"||"c")" 
    "(3:"a"|"b"|;|"c")" 
) do (
    set "var=%%~S" 
    call :validate 
) 
exit /b 

:validate 
setlocal enableDelayedExpansion 
cmd /v:on /c echo ^^^!var^^^!|findstr /r /c:"^([1-9][0-9]*:.*)$" >nul || (call :invalid FINDSTR fail& exit /b) 
if "!var:||=!" neq "!var!" (call :invalid double pipe fail& exit /b) 
for /f "delims=(:" %%N in ("!var!") do set "expectedCount=%%N" 
set "str=!var:*:=!" 
set "str=!str:~0,-1!" 
set foundCount=0 
for %%A in ("!LF!") do for /f eol^=^%LF%%LF%^ delims^= %%B in ("!str:|=%%~A!") do (
    if %%B neq "%%~B" (call :invalid sub-pattern fail& exit /b) 
    set /a foundCount+=1 
) 
if %foundCount% neq %expectedCount% (call :invalid count fail& exit /b) 
echo Valid: !var! 
exit /b 
:invalid 
echo Invalid - %*: !var! 
exit /b 

Éstos son los resultados después de ejecutar el archivo por lotes

Valid: (3:"a"|"c"|"c") 
Valid: (11:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k") 
Invalid - count fail: (4:"a"|"b"|"c") 
Invalid - count fail: (10:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k") 
Invalid - FINDSTR fail: (3:"a"|"b"|"c" 
Invalid - sub-pattern fail: (3:"a"|"b|c") 
Invalid - sub-pattern fail: (3:"a"|"b"|c) 
Invalid - double pipe fail: (3:"a"|"b"||"c") 
Invalid - sub-pattern fail: (3:"a"|"b"|;|"c") 


actualización

La rutina :validate se puede simplificar un poco al posponer la habilitación de expansión retardada hasta después de la CMD /V:ON tubo. Esto significa que ya no tengo que preocuparme por el doble escape del ! en el lado izquierdo de la tubería.

:validate 
cmd /v:on /c echo !var!|findstr /r /c:"^([1-9][0-9]*:.*)$" >nul || (call :invalid FINDSTR fail& exit /b) 
setlocal enableDelayedExpansion 
... remainder unchanged 
+0

Bueno, aunque no sabemos si el sexto caso de prueba es realmente falso. –

+0

@MatM - buen punto. Si debe ser válido, entonces la solución será significativamente más complicada. – dbenham

+0

@dbenham: la solución es correcta, ¡gracias! Me gustaría contarte sobre un proyecto que hice por lotes para tener tu opinión, ¡podría ser de interés común! – networkcode

2

Por lo que sé, findstr no es capaz de agrupar expresiones regulares, por lo que (|".*")* es un no-no. Si sabe cuántos bloques que tienes y duplicar su código como este

FINDSTR /R /C:"^([1-9][0-9]*:\"..*\"|\"..*\"|\"..*\")$" 

De esta manera, si está seguro de que el número de bloques es constante, teniendo las vacías "", si es necesario, a continuación, se puede comprobar por ella.

Las comillas dobles dentro de la expresión se ignoran a menos que las prefija con \.
La construcción ..* está destinada a reemplazar .+: uno o más caracteres.

+0

La cadena no puede contener un número constante de subpatrones, el número variable se comunica desde el primer número seguido de:. Quizás una solución sería validar inmediatamente la cadena con un FINDSTR y luego analizar el token de cada subparámetro con un FOR/F, si es correcto en contenido y número, ¿qué opinas? – networkcode

+0

@ user1125183 - Eso debería funcionar, pero es complicado. Ver [mi respuesta] (http://stackoverflow.com/a/12570555/1012053) – dbenham