问题
This is a question for the batch pro's i guess. Seems a lot of people do stumble over IP veriffication while batching, while just using windows built in functinallity, but no real code is to find.
At several places a findstr expression is findable, to identify a numerical string matching four sequences of numbers.
findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"
While there seems no better way to identify the string itself (because of limited regular expression support of findstr) this catches too false positives like 1234.2.3.4
or 999.999.999.999
and of course 0.0.0.0
.
It would "only" need to verify the found string further, e.g. with a sequence of for loops, and `make shure each found string octet is valid for IP rules.
- first octet between
1 and 254
- second and
- third between
0 and 255
- forth between
1 and 254
If one would then integrate a second part of verification into this code to identify further if found IP was one of the 3 private classes (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16
or to be precice one of: 10.0.0.0 to 10.255.255.255, 172.16.0.0 to 172.31.255.255 and 192.168.0.0 to 192.168.255.255) this would make a round function. And not to forget the special 127.x.x.x must get warned D).
To get IP4 complete a switch to choose if private or public IP shall get verified and returnvalues may tell closer what kind or even subnet masks have been recognized:
- first octet must be
255
- second and
- third and
- fourth one of
0,128,192,224,240,248,252,254,255
So overall this would be the pseudo routine:
a function taking input, no matter where from, just to be called, taking switch for usage and returning result
- take Switches for private or public IP range
- validate IP Syntax
- IP verification - recognized range and set return values
- private or
- pulic class
- subnet
- ...
- IP verification - recognized range and set return values
- set returnvalue, set errorlevel depending on switch
If the code does set nice usable return codes (like in example, return the recognized ip range) this would be an alltime function for everybody dealing with IP4's anyway. While i will extend those ip ranges myself, if only just the function will reliable return those "_return"-values like in example code.
Did i forget something?
Nobody did code this already?
SOLUTION: According to MC ND's example and Aacini's switch handling and subnetmask code i blew up the code, added errorhandling usage echo and other cases - here with some testing sample code included:
@echo off
setlocal enableextensions enabledelayedexpansion
rem try some ip addresses
for %%i in ("1.2.3.4" "0.1.2.3" "250.1024.1.2" "10.0.2.1" "127.0.0.1" "1.2.3.255" "172.16.17.18" "192.168.1.1" "255.128.240.0" "0.0.0.0" "something" "" ) do (
REM 1.2.3.4 is public / 0.1.2.3 is false all / 10.0.2.1 is private / 127.0.0.1 is local / 172.16.17.18 is private / 192.168.1.1 is private / 255.128.240.0 is subnet / 0.0.0.0 is false all (source net)
echo --------------- run one as default case assuming pulic with ret var -------------------
rem call default with a return variable
call :validateIP %%~i ret && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo --------------- run two with switch public -------------------
rem call with switch public
call :validateIP %%~i /public && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run three with switch private ---------------------
rem call with switch private
call :validateIP %%~i /private && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run four with switch private and ret variable ---------------------
rem call with switch private and return variable
call :validateIP %%~i /private ret && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run five with switch local and ret variable ---------------------
rem call with switch private and return variable
call :validateIP %%~i /local ret && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run six with switch subnet and ret variable ---------------------
rem call with switch private and return variable
call :validateIP %%~i /subnet ret && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run seven with switch source and ret variable ---------------------
rem call with switch private and return variable
call :validateIP %%~i /source ret && echo %%i is valid || echo %%i is invalid
echo return value: !ret!
echo --------------------------------------------
echo ------------ run eight with nothing ---------------------
rem call with switch private and return variable
call :validateIP && echo is valid || echo is invalid
echo return value: !ret!
echo --------------------------------------------
)
exit /b
:validateIP ipAddress [/ipRange] [returnVariable]
rem prepare environment
setlocal enableextensions enabledelayedexpansion
if "%~1"=="" goto USAGE
echo %~1| findstr /b /e /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" >nul
if errorlevel 1 goto USAGE
:afterusage
rem Initialize ip range as public
set "ipCASE=public"
rem Process switches
set "returnVar=%~2"
rem If second parameter start with slash...
if "%returnVar:~0,1%" equ "/" (
rem It is the /ipRange
set "ipCASE=%returnVar:~1%"
set "returnVar=%~3"
)
rem asume failure in tests : 0=pass 1=fail : same for return/errorlevel
set "_return=1"
set "_returnlevel=1"
set "subNETNumbers=0,128,192,224,240,248,252,254,255"
rem test if address conforms to ip address structure
echo %~1| findstr /b /e /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" >nul
rem if it conforms to structure, test each octet for range values
if not errorlevel 1 for /f "tokens=1-4 delims=." %%a in ("%~1") do (
if %%a gtr 0 if %%a lss 255 if %%b leq 255 if %%c leq 255 if %%d gtr 0 if %%d leq 254 set "_return=public"
if %%a equ 10 if %%b geq 0 if %%b lss 255 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 172 if %%b geq 16 if %%b lss 31 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 192 if %%b equ 168 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 127 if %%b geq 0 if %%b lss 255 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=local"
if %%a equ 255 if not "!subNETNumbers:%%b=!" equ "%subNETNumbers%" if not "!subNETNumbers:%%c=!" equ "%subNETNumbers%" if not "!subNETNumbers:%%d=!" equ "%subNETNumbers%" set "_return=subnetmask"
if %%a equ 0 set "_return=sourcenetwork"
)
rem set returnlevels depending on given switch
if "%ipCASE%"=="public" if "%_return%"=="public" (set "_returnlevel=0") else (set "_returnlevel=1")
if "%ipCASE%"=="private" if "%_return%"=="private" (set "_returnlevel=0") else (set "_returnlevel=1")
if "%ipCASE%"=="local" if "%_return%"=="local" (set "_returnlevel=0") else (set "_returnlevel=1")
if "%ipCASE%"=="subnet" if "%_return%"=="subnetmask" (set "_returnlevel=0") else (set "_returnlevel=1")
if "%ipCASE%"=="source" if "%_return%"=="sourcenetwork" (set "_returnlevel=0") else (set "_returnlevel=1")
REM OPTION1 set errorlevel
REM another correct way to set errorlevel would be to REM this line beneath and instead use _returnlevel with exit /b like in line REM OPTION2 - while this is interesting way to set it indirectly
if "%_returnlevel%"=="0" (ver > nul) else (set dummy 2> nul)
:endValidateIP
rem clean and return data/errorlevel to caller
endlocal & ( if not "%returnVar%"=="" set "%returnVar%=%_return%" ) & exit /b
REM OPTION2 endlocal & ( if not "%returnVar%"=="" set "%returnVar%=%_return%" ) & exit /b %_returnlevel%
:usage
echo.
echo Usage: call :validateIP [/ipRange] [returnVariable]
echo.
echo for example: call :validateIP 127.0.0.2 /local ret
echo.
echo if NO switch is given function assumes public,
echo switch and return var are optional
echo errorlevel depends and corresponds on given switch
echo known switches: /public, /private, /local, /subnet, /source
echo return var reflects syntax check, if return var is "1" the input was malformed anyhow
echo.
goto :afterusage
回答1:
Basic structure for ip validation. Adapt as needed
@echo off
setlocal enableextensions enabledelayedexpansion
rem try some ip addresses
for %%i in ("1.1.1.1" "0.1.1.1" "250.1024.1.1" "10.0.2.1" "something" "" ) do (
echo --------------------------------------------
rem call with a variable to get return value
call :validateIP %%~i ret
echo %%~i : return value : !ret!
rem call with or without variable to get errorlevel
call :validateIP %%~i && echo %%i is valid || echo %%i is invalid
)
exit /b
:validateIP ipAddress [returnVariable]
rem prepare environment
setlocal
rem asume failure in tests : 0=pass 1=fail : same for errorlevel
set "_return=1"
rem test if address conforms to ip address structure
echo %~1^| findstr /b /e /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" >nul
rem if it conforms to structure, test each octet for rage values
if not errorlevel 1 for /f "tokens=1-4 delims=." %%a in ("%~1") do (
if %%a gtr 0 if %%a lss 255 if %%b leq 255 if %%c leq 255 if %%d gtr 0 if %%d leq 254 set "_return=0"
)
:endValidateIP
rem clean and return data/errorlevel to caller
endlocal & ( if not "%~2"=="" set "%~2=%_return%" ) & exit /b %_return%
回答2:
there is no GNU BRE to validate dotted IPs. FINDSTR
s REGEX capabilities are below that. You can use grep for Windows and GNU ERE:
ECHO(%IP%|GREP -E "(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])"&&ECHO %IP% IS VALID/||%IP% IS NOT A VALID IP.
回答3:
I assumed you want to review IP numbers placed inside a text file. If the IP's appear at fixed positions inside lines in the file, for example, at third token like this:
IP number: 172.16.0.0/12
Then you may extract the IP number and separate its parts with a FOR /F command, and then review they in any way you wish:
setlocal EnableDelayedExpansion
set wantedNumbers=0,128,192,224,240,248,252,254,255
for /F "tokens=3" %%n in ('findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" theFile.txt') do (
for /F "tokens=1-4 delims=./" %%a in ("%%n") do (
if %%a neq 255 echo First octet is not 255 & goto error
if "!wantedNumbers:%%b=!" equ "%wantedNumbers%" echo Second octet bad & goto error
if "!wantedNumbers:%%c=!" equ "%wantedNumbers%" echo Third octet bad & goto error
if "!wantedNumbers:%%d=!" equ "%wantedNumbers%" echo Fourth octet bad & goto error
echo The IP %%n is correct
)
)
If the IP numbers are not at fixed positions, you need another, existent solution.
However, if the IP is stored in an individual variable (you have not indicated this point), just eliminate the first for
and replace %%n
by the value of the variable.
EDIT: New method added
Edit 2: List of numbers/ranges added
I devised a different way to solve this problem via the following subroutine:
@echo off
rem ValidateIP.bat: Validate an IP4 address
rem Antonio Perez Ayala
if "%~1" neq "" goto validateIP
echo Validate an IP4 address using several successive testings on it
echo/
echo call ValidateIP.bat ipAddress 1:ipRange1 2:ipRange2 ... N:ipRangeN
echo/
echo Each ipRange is comprised of an "errorlevel" followed by a colon and
echo 4 groups separated by dots of anyone of the following:
echo/
echo - An asterisk, that match any value between 0 and 255.
echo - A number, that match just that value.
echo - Two numbers separated by hypen, that match any value in that range.
echo - A list of numbers and/or ranges separated by number-sign (#).
echo/
echo At end, the value placed before the colon of the *last* matching ipRange
echo is returned in ERRORLEVEL; two or more ipRanges may return the same value.
echo If no ipRange is given, 1:*.*.*.* is assumed.
echo If no ipRange is matched, return zero.
echo/
echo Some examples:
echo/
echo call ValidateIp %%IPaddress%% 1:0-254.*.*.0-254 2:172.16-30.0-254.1-254
echo/
echo set subNET=0#128#192#224#240#248#252#254#255
echo call ValidateIP %%IPaddress%% 1:255.%%subNET%%.%%subNET%%.%%subNET%%
goto :EOF
:validateIP ipAddress [#:ipRange] ...
setlocal EnableDelayedExpansion
echo %~1| findstr /B /E /R "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" > NUL
if errorlevel 1 exit /B 0
for /F "tokens=1-4 delims=." %%a in ("%~1") do set p1=%%a& set p2=%%b& set p3=%%c& set p4=%%d
set "ipRanges=%* "
set ipRanges=%ipRanges:* =%
if not defined ipRanges set ipRanges=1:*.*.*.*
set ipRanges=%ipRanges::*=:0-255%
set ipRanges=%ipRanges:.*=.0-255%
set return=0
for %%a in (%ipRanges%) do for /F "tokens=1,2 delims=:" %%b in ("%%a") do (
set /A fields=0, i=0
set ipRange=%%c
for %%d in ("!ipRange:.=" "!") do (
set /A this=0, i+=1
set range=%%~d
for %%e in ("!range:#=" "!") do for /F "tokens=1-3 delims=-" %%i in ("!i!-%%~e-%%~e") do (
if !p%%i! geq %%j if !p%%i! leq %%k set this=1
)
set /A fields+=this
)
if !fields! equ 4 set return=%%b
)
exit /B %return%
Using previous subroutine, this particular problem may be solved this way:
set "subNET=0#128#192#224#240#248#252#254#255"
call ValidateIp %theIP% 1:0-254.*.*.0-254 2:10.0-254.0-254.0-254 2:172.16-30.0-254.1-254 2:192.168.0-254.0-254 3:127.0-254.0-254.0-254 4:255.%subNET%.%subNET%.%subNET% 5:0.*.*.*
if %errorlevel% equ 1 (
set ret=public
) else if %errorlevel% equ 2 (
set ret=private
) else if %errorlevel% equ 3 (
set ret=local
) else if %errorlevel% equ 4 (
set ret=subnetmask
) else if %errorlevel% equ 5 (
set ret=sourcenetwork
) else (
echo Invalid IP address
goto :EOF
)
Or in this shorter way:
set i=0
for %%a in (public private local subnetmask sourcenetwork) do (
set /A i+=1
set result[!i!]=%%a
)
set "subNET=0#128#192#224#240#248#252#254#255"
call ValidateIp %theIP% 1:0-254.*.*.0-254 2:10.0-254.0-254.0-254 2:172.16-30.0-254.1-254 2:192.168.0-254.0-254 3:127.0-254.0-254.0-254 4:255.%subNET%.%subNET%.%subNET% 5:0.*.*.*
if defined result[%errorlevel%] (
set ret=!result[%errorlevel%]!
) else (
echo Invalid IP address
goto :EOF
)
回答4:
This is a NEW answer to the NEW question in this same topic!
As I said you in my comment I need to understand what is the supposed operation of the code in order to fix it (otherwise, how could I do that?), but you not gave me a single description of your NEW code, so I can only guess...
So I guess that :validateIP
subroutine must return a numeric errorlevel value to the caller program as described in exit /?
command, and that it optionally return a string variable to the caller's environment. The code below do that:
:validateIP ipAddress [/ipRange] [returnVariable]
rem prepare environment
setlocal
rem Initialize ip range as public
set ipCASE=public
rem Process switches
set "returnVar=%~2"
rem If second parameter start with slash...
if "%returnVar:~0,1%" equ "/" (
rem It is the /ipRange
set "ipCASE=%returnVar:~1%"
set "returnVar=%~3"
)
echo ipcase: %ipCase%
rem asume failure in tests : 0=pass 1=fail : same for return/errorlevel
set "_return=1"
set "_returnlevel=1"
rem test if address conforms to ip address structure
echo %~1| findstr /b /e /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" >nul
rem if it conforms to structure, test each octet for range values
if not errorlevel 1 for /f "tokens=1-4 delims=." %%a in ("%~1") do (
if %%a gtr 0 if %%a lss 255 if %%b leq 255 if %%c leq 255 if %%d gtr 0 if %%d leq 254 set "_return=public"
if %%a equ 10 if %%b geq 0 if %%b lss 255 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 172 if %%b geq 16 if %%b lss 31 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 192 if %%b equ 168 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=private"
if %%a equ 127 if %%b geq 0 if %%b lss 255 if %%c geq 0 if %%c lss 255 if %%d gtr 0 if %%d leq 254 set "_return=local"
)
rem set errorlevels
if "%ipCASE%"=="public" if "%_return%"=="public" (set "_returnlevel=0") else (set "_returnlevel=1")
if "%ipCASE%"=="private" if "%_return%"=="private" (set "_returnlevel=0") else (set "_returnlevel=1")
:endValidateIP
rem clean and return data/errorlevel to caller
endlocal & ( if not "%returnVar%"=="" set "%returnVar%=%_return%" ) & exit /b %_returnlevel%
However, when I tested this subroutine with your testing code I got strange results. I reviewed the entire code and discovered several problems not in the subroutine, but in the testing code; for example, echo %errorlevel%
inside the main for
loop always will show 0, and the invocation of :validateIP ipAddress [/ipRange] [returnVariable]
don't follow this format in 3 of 4 cases and sometimes don't include the return variable, but the calling code always show it, etc...
Of course, these problems have no relation with the original theme of this topic nor with your last request and I already spent too much time in this matter, so I didn't solved they...
来源:https://stackoverflow.com/questions/20299266/ip-verification-in-batch-script-first-match-by-findstr-secondly-verify-by-for