I want to match a variable to part of the contents of another variable in batch. Here is some pseudo code for what I want to do.
set h= Hello-World
set f= This is a Hello-World test
if %h% matches any string of text in %f% goto done
:done
echo it matched
Does anybody know how I could accomplish this?
Based off of this answer here, you can use the FINDSTR command to compare the strings using the /C switch (modified from the linked answer so you don't have to have a separate batch file for comparing the strings):
#ECHO OFF
set h=Hello-World
set f=This is a Hello-World test
ECHO Looking for %h% ...
ECHO ... in %f%
ECHO.
echo.%f% | findstr /C:"%h%" 1>nul
if errorlevel 1 (
ECHO String "%h%" NOT found in string "%f%"!
) ELSE (
ECHO String "%h%" found in string "%f%"!
)
ECHO.
PAUSE
If the following conditions are met:
The search is case insensitive
The search string does not contain =
The search string does not contain !
then you can use:
#echo off
setlocal enableDelayedExpansion
set h=Hello-World
set f=This is a Hello-World test
if "!f:*%h%=!" neq "!f!" (
echo it matched
) else (
echo it did not match
)
The * preceding the search term is only needed to allow the search term to start with *.
There may be a few other scenarios involving quotes and special characters where the above can fail. I believe the following should take care of such problems, but the original constraints still apply:
#echo off
setlocal enableDelayedExpansion
set h=Hello-World
set f=This is a Hello-World test
for /f delims^=^ eol^= %%S in ("!h!") do if "!f:*%%S=!" neq "!f!" (
echo it matched
) else (
echo it did not match
)
Here's another way:
#echo off
set "h=Hello-World"
set "f=This is a Hello-World test"
call set "a=%%f:%h%=%%"
if not "%a%"=="%f%" goto :done
pause
exit /b
:done
echo it matched
pause
Related
I have a text file including filename specifications formatted as following, filename-yyyymmdd
source.txt
IMG-20190601
IMG-20190602
IMG-20190603
...
I want to read this file in order to compare the dates with a reference date and do some action depending the result. IMG is always the same, only the date is changing.
For this purpose I am trying to find the filename date into each line I am reading to compare it with today.
I did not succeed to find the right syntax, I found that extracting a substring can be done with
set SUBSTRING=%VAR:~POSITION,SIZE%
but it is not working with %%variable type.
Any help is welcome.
My code:
set comparedate=20190702
set /A i=0
for /F "usebackq delims=" %%a in (source.txt) do (
set /A i+=1
rem call echo %%i%%
rem call echo %%a
set datefile=%%a:~4,8 # the line that is not working
if %datefile% geq %comparedate% (goto here) else (goto there)
:here
echo do something
:there
echo do something else
)
Implemented my suggestions from the comments to your code. Also avoided delayed expansion by using the tokens directly (together with another method to get the date string from the filenames (by splitting at the hyphen)):
#echo off
set comparedate=20190702
set /A i=0
for /F "tokens=1,* delims=-" %%a in (source.txt) do (
ECHO FileDate=%%b
ECHO FileName=%%a-%%b [in case you need it]
if "%%b" geq "%comparedate%" (
echo do something
) else (
echo do something else
)
)
In a batch script, I need to split only the last string match in a variable in a loop until I no longer have the string match.
Input=Level1/Level2/Level3/Level4/LevelN
(where N can be any number)
Output:
Level1/LeveL2/Level3/Level4
Level1/LeveL2/Level3
Level1/Level2
Level1
I have tried the usual "for /f "delims=/"" loops, but they only output each split of the input variable on an individual line. Besides, the value of "N" can vary. So I can't set the number of tokens to a certain value.
Please help.
This site don't works that way. You must post some code and explain the problems you have with it. In this way you can understand the changes made to your own code. If you request us for code, any code, then you could receive one ("any code") like this:
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
:loop
set "Output="
set "part=%Input:/=" & set "Output=!Output!/!part!" & set "part=%"
set "Input=%Output:~1%"
if "%Input%" equ "~1" goto exitLoop
echo "%Input%"
goto loop
:exitLoop
Here is a nice recursive approach that makes use of the ~ modifiers, assuming that the input string is provided as a quoted ("") command line argument, which does not begin with /, does not contain two consecutive // and none of the characters ", \, *, ?, <, >:
#echo off
rem // Store argument in variable:
set "INPUT=%~1"
if not defined INPUT exit /B
rem /* Precede with `\` and replace each `/` by `\`, so the resulting string appears to
rem be an absolute path, which can be split by `~` modifiers of `for` variables;
rem the inner `for` loop resolves the split path and removes any `\.` suffix: */
for %%I in ("\%INPUT:/=\%") do for %%J in ("%%~pI.") do set "REST=%%~pnxJ"
rem // Revert replacement of every `/` by `\` and remove the previously preceded `\`:
set "REST=%REST:\=/%"
set "REST=%REST:*/=%"
rem // If there is a string left, output it and call this script recursively:
if defined REST (
setlocal EnableDelayedExpansion
echo(!REST!
endlocal
call "%~f0" "%REST%"
)
A more classical approach, just for fun...
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
rem Split the string, join it again and store partial results
set "aux="
set "n=0"
for %%a in ("%Input:/=" "%") do (
set "aux=!aux!/%%~a"
set /A n+=1
set "out[!n!]=!aux:~1!"
)
rem Show results in reverse order
set /A n-=1
for /L %%i in (%n%,-1,1) do echo !out[%%i]!
This is easily done with a regex in PowerShell. Since the regex is greedy be default, it will get everything up to the last SOLIDUS and store it in the $1 group.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP "'%S%' -replace '(.*/).*', '$1'"') DO (
SET "RESULT=%%a"
)
ECHO RESULT is set to %RESULT%
Revision:
This outputs all groups and not just the last one.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP -Command ^
"$a = '%S%'.Split('/'); " ^
"for ($i = $a.Count - 2; $i -ge 0; $i--) { $a[0..$i] -join '/' }"') DO (
ECHO %%a
)
I'm having issues with a simple piece of batch program in a for loop.
basically, the program should for every file in a folder, if the file name begins with 'LOTTR' it should copy the file to the location Y:\ELCWork1-Rus. if not then it should be copied to Y:\ELCWork2-Oth. I've used 2 if's rather than an if..else for future substring comparisons...
I need to compare an evaluated substring to a fixed value.
In the code below - the logic always fails to match. I've tried with and without double quotes and set the variable inside and outside the for loop.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set %c=LOTTR
for %%s in (*.csv) do (
set tmp1=%%s
set vt1=!tmp1:~0,5!
set vt2=!tmp1:~0,55!
set "cmv=LOTTR"
echo !tmp1:~0,10!
echo !vt1!
echo %%c
if /I "!vt1!"=="LOTTR" (
echo "Matches"
Xcopy !tmp1! "Y:\ELCWork1-Rus"
)
if /I NOT "!vt1!"=="LOTTR" (
echo "No Match"
Xcopy !tmp1! "Y:\ELCWork2-Oth"
)
)
You have put a space after the line set vt1=!tmp1:~0,5!. The string becomes "LOTTR " if the file starts with "LOTTR":
#echo off
setlocal ENABLEDELAYEDEXPANSION
for %%s in (*.csv) do (
set tmp1=%%s
set vt1=!tmp1:~0,5!
if /I "!vt1!"=="LOTTR" (
echo "Matches"
Xcopy !tmp1! "Y:\ELCWork1-Rus"
)
if /I NOT "!vt1!"=="LOTTR" (
echo "No Match"
Xcopy !tmp1! "Y:\ELCWork2-Oth"
)
)
The last word in every texts in testing.txt is "< /a>", I echo out the the word and it seems to be no problem, but when I echo out in the for loop, cmd gave me this error : "The system cannot find the file specified."
I know the problem is on the "<" and ">" sign, it stands for redirection, that's how the error created. How am I going to make cmd think I'm working with a string instead of redirection?
#echo off
setlocal EnableDelayedExpansion
set "remove_char=< /a>"
echo !remove_char!
for /f "skip=2 tokens=6*" %%a in (testing.txt) do (
set "string=%%a %%b"
set string=!string:%remove_char%=!
echo !string!
)
pause >nul
If you want to use < and > symbols as variables you have to change them to ^< or ^>
Otherwise they will be treated as Input or Output!
Maybe you can replace this line
set string=!string:%remove_char%=!
with
for %%i in (!remove_char!) do (set string=!string:%%i=!)
Besides SED, how can an equal sign be replaced?
And how can I use a string variable in string replacement?
Consider this example:
For /F "tokens=*" %%B IN (test.txt) DO (
SETLOCAL ENABLEDELAYEDEXPANSION
SET t=is
SET old=%%B
SET new=!old:t=!
ECHO !new!
ENDLOCAL
)
:: SET new=!old:==!
Two problems:
First, I cannot use the variable %t% in !:=!.
SET t=is
SET old=%%B
SET new=!old:t=!
Second, I cannot replace the equal sign in the command line
SET new=!old:==!
I just created a simple solution for this myself, maybe it helps someone.
The disadvantage (or advantage, depends on what you want to do) is that multiple equal signs one after another get handled like one single equal sign. (example: "str==ing" gives the same output as "str=ing")
#echo off
set "x=this is=an test="
echo x=%x%
call :replaceEqualSign in x with _
echo x=%x%
pause&exit
:replaceEqualSign in <variable> with <newString>
setlocal enableDelayedExpansion
set "_s=!%~2!#"
set "_r="
:_replaceEqualSign
for /F "tokens=1* delims==" %%A in ("%_s%") do (
if not defined _r ( set "_r=%%A" ) else ( set "_r=%_r%%~4%%A" )
set "_s=%%B"
)
if defined _s goto _replaceEqualSign
endlocal&set "%~2=%_r:~0,-1%"
exit /B
As you have seen, you use the function like this:
call :replaceEqualSign in variableName with newString
The setlocal enableDelayedExpansion should be moved after your old=%%B assignment in case %%B contains !.
The "t" problem is easy to solve within a loop by using another FOR variable
For /F "tokens=*" %%B IN (test.txt) DO (
SET t=is
SET old=%%B
SETLOCAL ENABLEDELAYEDEXPANSION
for /f %%T in ("!t!") do SET new=!old:%%T=!
ECHO !new!
ENDLOCAL
)
There is no simple native batch solution for replacing =. You can iterate through the string, character by character, but that is slow. Your best bet is probably to switch to VBScript or JScript, or use a non-native utility.
If you really want to do this using pure Windows batch commands, there are a couple of interesting ideas at http://www.dostips.com/forum/viewtopic.php?t=1485
UPDATE: The latest version is here: https://github.com/andry81/contools (https://github.com/andry81/contools/tree/HEAD/Scripts/Tools/std/)
You can use some sequence to temporary replace special characters by placeholders like ?00, ?01, ?02 and ?03. I basically use these set of scripts:
replace_sys_chars.bat:
#echo off
rem Description:
rem Script to replace ?, !, %, and = characters in variables by respective
rem ?00, ?01, ?02 and ?03 placeholders.
setlocal DISABLEDELAYEDEXPANSION
set "__VAR__=%~1"
if "%__VAR__%" == "" exit /b 1
rem ignore empty variables
call set "STR=%%%__VAR__%%%"
if "%STR%" == "" exit /b 0
set ?01=!
call set "STR=%%%__VAR__%:?=?00%%"
set "STR=%STR:!=?01%"
setlocal ENABLEDELAYEDEXPANSION
set STR=!STR:%%=?02!
set "STR_TMP="
set INDEX=1
:EQUAL_CHAR_REPLACE_LOOP
set "STR_TMP2="
for /F "tokens=%INDEX% delims== eol=" %%i in ("/!STR!/") do set STR_TMP2=%%i
if "!STR_TMP2!" == "" goto EQUAL_CHAR_REPLACE_LOOP_END
set "STR_TMP=!STR_TMP!!STR_TMP2!?03"
set /A INDEX+=1
goto EQUAL_CHAR_REPLACE_LOOP
:EQUAL_CHAR_REPLACE_LOOP_END
if not "!STR_TMP!" == "" set STR=!STR_TMP:~1,-4!
(
endlocal
endlocal
set "%__VAR__%=%STR%"
)
exit /b 0
restore_sys_chars.bat:
#echo off
rem Description:
rem Script to restore ?, !, %, and = characters in variables from respective
rem ?00, ?01, ?02 and ?03 placeholders.
setlocal DISABLEDELAYEDEXPANSION
set "__VAR__=%~1"
if "%__VAR__%" == "" exit /b 1
rem ignore empty variables
call set "STR=%%%__VAR__%%%"
if "%STR%" == "" exit /b 0
setlocal ENABLEDELAYEDEXPANSION
set STR=!STR:?02=%%!
set STR=!STR:?03==!
(
endlocal
set "STR=%STR%"
)
set "STR=%STR:?01=!%"
set "STR=%STR:?00=?%"
(
endlocal
set "%__VAR__%=%STR%"
)
exit /b 0
Example:
#echo off
setlocal DISABLEDELAYEDEXPANSION
for /F "usebackq tokens=* delims=" %%i in ("test.txt") do (
set VALUE=%%i
call :PROCESS
)
exit /b 0
:PROCESS
if "%VALUE%" == "" exit /b 0
set "VALUE_=%VALUE%"
call replace_sys_chars.bat VALUE_
rem do variable arithmetic here as usual
if not "%VALUE_:?00=%" == "%VALUE_%" echo."%VALUE%" having ?
if not "%VALUE_:?01=%" == "%VALUE_%" echo."%VALUE%" having !
if not "%VALUE_:?02=%" == "%VALUE_%" echo."%VALUE%" having %%
if not "%VALUE_:?03=%" == "%VALUE_%" echo."%VALUE%" having =
rem restore it
call restore_sys_chars.bat VALUE_
echo "VALUE=%VALUE_%"
echo.---
test.txt:
111/222
AAA=BBB
CCC=%DDD%
EEE=!FFF!
FFF=?00?01?02?03
Result:
"VALUE=111/222"
---
"AAA=BBB" having =
"VALUE=AAA=BBB"
---
"CCC=%DDD%" having %
"CCC=%DDD%" having =
"VALUE=CCC=%DDD%"
---
"EEE=!FFF!" having !
"EEE=!FFF!" having =
"VALUE=EEE=!FFF!"
---
"FFF=?00?01?02?03" having ?
"FFF=?00?01?02?03" having =
"VALUE=FFF=?00?01?02?03"
---
Features:
You can continue use standard batch variable arithmetic between conversions
You can use character placeholders (?00, ?01, ?02, ?03) as plain variable values
Why not use Edlin? I could not find a way to do this with one initial file and no errors from Edlin, but just ignore them with NUL:.
Strangly, the TYPE %0 includes the whole file even if there's an end of file character between the = and !, using TYPE on the batch file after it has run will not work the same way.
#ECHO OFF
GOTO skip
1,1r=!
e
:skip
SET "new==old============="
ECHO %new% > %TEMP%\var.tmp
TYPE %0 > %TEMP%\edlin.tmp
EDLIN %TEMP%\var.tmp < %TEMP%\edlin.tmp > NUL:
SET /P newnew=<%TEMP%\VAR.TMP
ECHO %newnew%
ERASE %TEMP%\VAR.TMP
ERASE %TEMP%\VAR.BAK
ERASE %TEMP%\edlin.tmp
I was looking into this, because I needed to get rid of = in a string like "test=goingon"
I found that calling a next batchfile with test=goingon as parameters, I have parameters 1, "test" and 2, "goingon", in that batchfile.
So:
batchfile 1:
#echo off
call test2.bat test=goingon
batchfile2:
echo arg1: %1
echo arg2: %2
result:
arg1: test
arg2: goingon
I used Bosj's idea to come up with this. It works.
set s=Abra=Cadabra
echo now you see it %s%
call :ReplaceEqual %s%
echo now you don't %s%
exit /b
:ReplaceEqual
set s=%1_%2
exit /b
My answer from another post, but it applies here, too:
There is an alternative that is easier. Instead of passing in a value that contains an equals sign, try something like a colon instead. Then, through the ability to modify that value (the colon), you can convert it back into an equals. Here is an example:
#echo off
set VALUE1=%1
set VALUE2=%VALUE1::==%
echo value1 = %VALUE1%
echo value2 = %VALUE2%
When you run the batch file, call it like this:
C:\>myBatch name:someValue
The output would be:
value1 = name:someValue
value2 = name=someValue
If the name or value contains a space, you will have other issues to address, though. You will need to wrap the entire string in double quotes. But, then you have the issue of needing to get rid of them. This can also be handled, like this:
#echo off
cls
set PARAM=%1
set BASE=%PARAM:"=%
set PAIR=%BASE::==%
rem Either of these two lines will do the same thing - just notice the 'delims'
rem for /f "tokens=1,2 delims=:" %%a in ("%BASE%") do set NAME=%%a & set VALUE=%%b
rem for /f "tokens=1,2 delims==" %%a in ("%PAIR%") do set NAME=%%a & set VALUE=%%b
for /f "tokens=1,2 delims=:" %%a in ("%BASE%") do set NAME=%%a & set VALUE=%%b
echo param = %PARAM%
echo base = %BASE%
echo pair = %PAIR%
echo name = %NAME%
echo value = %VALUE%
When running this batch file like this:
C:\>myBatch "some name:another value"
The output will be:
param = "some name:another value"
base = some name:another value
pair = some name=another value
name = some name
value = another value
Hope that helps others in their quest to win the fight with batch files.
Mike V.