50} How do I get the position of a substring in a string?
One potential, pure script solution:
@echo off & setlocal enableextensions
rem 01234567890123456789
set a_=Hello world, Hello.
set b_=ll
call :InStrFN "%a_%" "%b_%" position_
echo The first position of "%b_%" in "%a_%" is %position_%
endlocal & goto :EOF
::
::===============================================================
:: A subroutine to find the first position of a searchstring in a
:: mainstring. If not found returns -1. If found, the indexing of
:: the position starts from 0 in accordance with SET conventions.
::
:InStrFN
setlocal enableextensions enabledelayedexpansion
::
:: Get the mainstring and the searchstring, get rid of possible quotes
set MainStr=%1
set MainStr=%MainStr:"=%
set SearchStr=%2
set SearchStr=%SearchStr:"=%
::
:: Get the length of the searchstring
set rest_=%SearchStr%
set /a SearchLength_=0
:_length_loop
set /a SearchLength_+=1
set rest_=%rest_:~1%
if defined rest_ goto :_length_loop
::
:: Check for a non-match
echo %MainStr%|find "%SearchStr%">nul
if %errorlevel% EQU 1 (endlocal & set /a %3=-1& goto :EOF)
::
:: Match the searchstring with mainstring substrings
set /a i_=0
:_position_loop
if "!MainStr:~%i_%,%SearchLength_%!"=="%SearchStr%" (
endlocal & set /a %3=%i_%& goto :EOF)
set /a i_+=1
goto _position_loop
endlocal & goto :EOF
The output will be
D:\TEST>cmdfaq
The first position of "ll" in "Hello world, Hello." is 2
There are other solution options:
@echo off & setlocal enableextensions enabledelayedexpansion
set a_=Hello world, Hello.
set b_=ll
set instr=-1
for /l %%i in (0,1,255) do (
set str=!a_:~%%i!
if defined str (
echo !str!|findstr /b /l "%b_%">nul
if !errorlevel! EQU 0 if !instr! EQU -1 set instr=%%i
)
)
echo The first position of "%b_%" in "%a_%" is %instr%
endlocal & goto :EOF
To find the last position:
@echo off & setlocal enableextensions
set a_=Hello world, Hello.
set b_=ll
call :InStrLastFN "%a_%" "%b_%" position_
echo The last position of "%b_%" in "%a_%" is %position_%
endlocal & goto :EOF
::
:: ============================================================
:InStrLastFN
setlocal enableextensions enabledelayedexpansion
echo %1|findstr %2>nul
if %errorlevel% EQU 1 (endlocal & set %3=-1& goto :EOF)
set rest_=%1
set /a instr_=-1
:_loop
set rest_=%rest_:~1%
echo !rest_!|findstr %2>nul
if %errorlevel% EQU 1 (endlocal & set %3=%instr_%& goto :EOF)
set /a instr_ +=1
goto _loop
endlocal & goto :EOF
The output will be
D:\TEST>cmdfaq
The last position of "ll" in "Hello world, Hello." is 15
There are other solution options:
@echo off & setlocal enableextensions enabledelayedexpansion
set a_=Hello world, Hello.
set b_=ll
set instr=-1
for /l %%i in (0,1,255) do (
set str=!a_:~%%i!
if defined str (
echo !str!|findstr /b /l "%b_%">nul
if !errorlevel! EQU 0 set instr=%%i
)
)
echo The last position of "%b_%" in "%a_%" is %instr%
endlocal & goto :EOF
If you can get hold of QBASIC, the first position can also be solved
as follows
@echo off & setlocal enableextensions
if defined ProgramW6432 (
echo/
echo Exiting: %~f0 is incompatible with 64-bit Windows
goto :EOF)
::
set a_=Hello world, Hello.
set b_=ll
::
> tmp$$$.bas echo OPEN "tmp$$$.cmd" FOR OUTPUT AS #1
>>tmp$$$.bas echo PRINT #1, "@set p_=";LTRIM$(STR$(INSTR("%a_%", "%b_%")))
>>tmp$$$.bas echo CLOSE #1
>>tmp$$$.bas echo SYSTEM
::
qbasic /run tmp$$$.bas
call tmp$$$.cmd
::
echo The position of "%b_%" in "%a_%" is %p_%
::
for %%f in (tmp$$$.bas tmp$$$.cmd) do if exist %%f del %%f
endlocal & goto :EOF
The output will be (note the different indexing!)
D:\TEST>cmdfaq
The position of "ll" in "Hello world, Hello." is 3
Alternatively, a Visual Basic aided command line script solution
could be used:
@echo off & setlocal enableextensions
:: Build a Visual Basic Script
findstr "'VBS" "%~f0"|findstr /v "findstr" > tmp$$$.vbs
::
set a_=Hello world, Hello.
set b_=ll
::
:: Run the Visual Basic Script
cscript //nologo tmp$$$.vbs
::
:: Run the auxiliary CMD script
call tmp$$$.cmd
::
echo The position of "%b_%" in "%a_%" is %p_%
::
for %%f in (tmp$$$.vbs tmp$$$.cmd) do if exist %%f del %%f
endlocal & goto :EOF
'
'................................................................
'The Visual Basic Script
'
Const ForReading = 1, ForWriting = 2, ForAppending = 8 'VBS
Dim a, b, p, FSO, fout 'VBS
Set WshShell = WScript.CreateObject("WScript.shell") 'VBS
a=WshShell.ExpandEnvironmentStrings("%a_%") 'VBS
b=WshShell.ExpandEnvironmentStrings("%b_%") 'VBS
p = InStr(1, a, b, vbTextCompare) 'VBS
Set FSO = CreateObject("Scripting.FileSystemObject") 'VBS
Set fout = FSO.OpenTextFile("tmp$$$.cmd", ForWriting, true) 'VBS
fout.WriteLine "@set p_=" & p 'VBS
The output would (again) be
D:\TEST>cmdfaq
The position of "ll" in "Hello world, Hello." is 3
More concisely
@echo off & setlocal enableextensions
:: Build a Visual Basic Script
set vbs_=%temp%\tmp$$$.vbs
set skip=
findstr "'%skip%VBS" "%~f0" > "%vbs_%"
::
set a_=Hello world, Hello.
set b_=ll
::
:: Run the Visual Basic Script
for /f %%p in ('
cscript //nologo "%vbs_%" "%a_%" "%b_%"') do (
set p_=%%p)
:: Clean up
for %%f in ("%vbs_%") do if exist %%f del %%f
::
:: Display the result
echo The position of "%b_%" in "%a_%" is %p_%
endlocal & goto :EOF
'
'...............................................
'The Visual Basic Script
'
set arg = WScript.Arguments 'VBS
a = arg(0) 'VBS
b = arg(1) 'VBS
Wscript.Echo InStr(1, a, b, 1) 'VBS
Or even more concisely
@echo off & setlocal enableextensions
::
set a_=Hello world, Hello.
set b_=ll
::
:: Build and run a Visual Basic Script
set vbs_=%temp%\tmp$$$.vbs
>"%vbs_%" echo Wscript.Echo InStr(1, "%a_%", "%b_%", 1)
for /f %%p in ('cscript //nologo "%vbs_%"') do set p_=%%p
for %%f in ("%vbs_%") do if exist %%f del %%f
::
:: Display the result
echo The position of "%b_%" in "%a_%" is %p_%
endlocal & goto :EOF