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
)
)
Related
I am trying to find a particular line from an XML file and extract the value using string manipulation operations.
Below is the code I am trying.
#echo off
setlocal enabledelayedexpansion
::Expected line is "<filename>c:\temp\file1</filename>"
for /f "tokens=*" %%i in ('findstr /i "filename" file1.props') do (
SET LINE=%%i
)
echo !LINE!
SET FILENAME=!LINE:<filename>=!
SET FILENAME=%FILENAME:</filename>=%
ECHO !FILENAME!
And the output is:
<filename>c:\temp\file1</filename>
The system cannot find the file specified.
ECHO is off.
I actually want this value c:\temp\file1
Someone please help me correct the code or please suggest any other simpler way.
The problem is the execution of set. The parser interprets the > and < as redirection, so it will fail with a syntax error. Use quotes to process it as intended (`set "var=value"):
#echo off
setlocal enabledelayedexpansion
REM echo ^<filename^>c:\temp\file1^</filename^>>file1.props
::Expected line is "<filename>c:\temp\file1</filename>"
for /f "tokens=*" %%i in ('findstr /i "filename" file1.props') do (
SET "LINE=%%i"
)
echo !LINE!
SET "FILENAME=!LINE:<filename>=!"
SET "FILENAME=%FILENAME:</filename>=%"
ECHO !FILENAME!
Output is:
<filename>c:\temp\file1</filename>
c:\temp\file1
I have a situation where I'm trying to keep a static list of related items in a string and parse them out as sets in a bat file.
SET RootPath=C:\Users\woodh\test\
SET FromPath=StuffFrom\
SET ToPath=StuffTo\
SET CTLNames='text1.txt,red_text1:text2.txt,white_text2:text3.txt,blue_text3:'
With CTLNames containing pairs of entries to be parsed and consumed in the job.
I did the following
:Step20
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each pair at a time
rem -----------------------------------------------------------------
FOR /F "delims=:" %%f IN (%CTLNames%) DO (
IF NOT "%%f" == "" (
CALL:BreakEntry "%%f"
)
)
:Finish
rem ----------------------------------------------------------------
rem -- Finish
rem ----------------------------------------------------------------
goto end
:BreakEntry
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each entry one at a time
rem -----------------------------------------------------------------
Set EntryLine=%~1
IF NOT "%EntryLine%" == "" (
ECHO %EntryLine%
FOR /F "tokens=1,2 delims=," %%a IN ("%EntryLine%") DO (
ECHO %%a
ECHO %%b
CALL:MoveThisFile %%a, %%b
)
)
goto:eof
But It's only processing the first pair of names and not continuing through the rest of the list.
Your question is confusing. You didn't explained what exactly is the purpose of your code nor the expected output, so we can only guess. So I guess that you have a series of pairs of values separated by colon, and that each pair of values is separated by comma. This way, the problem with your code is that for /F command does not iterate over several values when just one string is processed: the string is divided accordingly to "tokens and delims" options and the command is executed just one time. You need to use a different method to process all substring in the string.
This is the way I would do it:
#echo off
setlocal
SET "CTLNames=text1.txt,red_text1:text2.txt,white_text2:text3.txt,blue_text3:"
for %%f in ("%CTLNames::=" "%") do (
for /F "tokens=1,2 delims=," %%a in (%%f) do (
echo %%a
echo %%b
echo CALL :MoveThisFile %%a, %%b
)
)
I suggest you to remove the #echo off line and execute the program, so you may review what exactly is executed.
The reason why it doesn't work as expected (it only prints the 1st pair), is because for /f works on lines; CTLNames only consists of a line so a single iteration is needed.
The confusing part is that it still printed the 1st pair...that is because it actually did the split (on the 1st :) but by default for only cares about the 1st token (before the delim) and drops the rest. You can convince yourself by changing the line to:
FOR /F "tokens=* delims=:" %%f IN (%CTLNames%) DO (
you'll see that the value of %%f (because we instructed it to take all the tokens into account) is the whole line.
The reason why I asked if the COLON(:) is mandatory as a separator between pairs, is because you can also iterate over a non numeric list - no /f flag, but here you can't specify the delimiter so you must use a regular one: SPACE( ), COMMA(,), SEMICOLON(;), TAB, and maybe others (anyway COLON is not one of them) - so this loop:
for %%f in (text1.txt:red_text1 text2.txt:white_text2 text3.txt:blue_text3;) do (
echo %%f
)
- note that I used 3 separators: TAB, SPACE and SEMICOLON in the for loop (not sure how visible it is) -
would yield:
text1.txt:red_text1
text2.txt:white_text2
text3.txt:blue_text3
Or you could use regular separators everywhere, and give up at the pair concept altogether, but I don't know if this is what you want.
I wasn't able to solve the problem using COLON as a separator from a single for loop, but I was able to find a way. Here's your script (slightly modified):
#ECHO OFF
rem ECHO %CTLNames%
CALL :Step20 "%CTLNames%"
GOTO :eof
:Step20
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each pair at a time
rem -----------------------------------------------------------------
IF "" == "%~1" GOTO :eof
FOR /F "tokens=1* delims=:" %%f IN ("%~1") DO (
rem echo f: %%f
CALL :BreakEntry "%%f"
CALL :Step20 "%%g%
)
GOTO :eof
:BreakEntry
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each entry one at a time
rem -----------------------------------------------------------------
Set EntryLine=%~1
ECHO %EntryLine%
FOR /F "tokens=1,2 delims=," %%a IN ("%EntryLine%") DO (
ECHO %%a
ECHO %%b
rem CALL :MoveThisFile %%a, %%b
)
GOTO :eof
The main thing is (besides other small changes) that Step20 is a recursive function (label), and it uses the for loop to split the line, it processes the 1st token, then it calls itself on the remaining tokens (until there are no more left).
Note: the single quotes surrounding CTLNames should be removed.
I have a file with a lot of text.
EG
Hello
This is my file
this is the end of the file
I need a script that will search the file and pull out all words (just the words and not the line into another file) that contain for example the letter e
In this case the new file would look like
Hello
file
the
end
the
file
It may also need to search for as another example bh. (including the full stop) so a file with the following
hello
bh.ah1
my file
the end
would produce a file with
bh.ah1
hope this is enough detail
#ECHO OFF
SETLOCAL
SET "target=%~1"
FOR /f "delims=" %%a IN (q22560073.txt) DO CALL :findem %%a
GOTO :EOF
:findem
SET candidate=%1
IF NOT DEFINED candidate GOTO :EOF
ECHO %1|FIND /i "%target%" >NUL
IF NOT ERRORLEVEL 1 ECHO(%1
shift
GOTO findem
I used a file named q22560073.txt for my testing.
To find the text string, use
thisbatch text
so
thisbatch e
would find the first list and
thisbatch bh.
the second.
(I combined both sample testfiles as q22560073.txt)
the /i in the find command makes the test case-insensitive.
To output to a file, simply use
thisbatch text >"filename"
where the "rabbits ears" are only required if the filenames contains spaces and other problematic characters, but do no harm in any case.
This should work for a target of any alphabetic or numeric combination plus full stop. It will not work with characters that have a special meaning to cmd.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "target=%~1"
FOR /f "delims=" %%a IN (q22560073.txt) DO (
SET "line=%%a"
SET "line=!line:(= !"
SET "line=!line:)= !"
CALL :findem !line!
)
GOTO :EOF
:findem
SET candidate=%1
IF NOT DEFINED candidate GOTO :EOF
ECHO %1|FIND /i "%target%" >NUL
IF NOT ERRORLEVEL 1 ECHO(%1
shift
GOTO findem
revised on further information.
#echo off
set "searchfor=bh."
for /f "delims=" %%i in (t.t) do (
for %%j in (%%i) do (
echo %%j|find "%searchfor%" >nul && echo %%j
)
)
for every line (%%i) do
for every word in this line (%%j) do
if searchstring found then echo word
EDIT to your comment: replace the ( with a space in the line, before processing words
#echo off
setlocal enabledelayedexpansion
set "searchfor=bh."
for /f "delims=" %%i in (t.t) do (
set t=%%i
set t=!t:(= !
for %%j in (!t!) do (
echo %%j|find "%searchfor%" >nul && echo %%j
)
)
You can do this for more characters with additional lines like set t=!t:(= ! (replace ( with )
I have a batch file where I need to write a variable into a specific line of a text file and override what is all ready in that line. I have the code to read specific lines from the file maybe I could switch it around to also write?
Reading lines code:
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (variables.txt) do (
set /a N+=1
set v!N!=%%a
)
set variable1=!v1!
set variable2=!v2!
set variable3=!v3!
set variable4=!v4!
I've tried to add echo %variable1% > !v4! something like that but it doesn't work.
I figured it out!! Here is the code for anyone else who might ever need it.
#echo off
setlocal enableextensions enabledelayedexpansion
set inputfile=variables.txt
set tempfile=%random%-%random%.tmp
copy /y nul %tempfile%
set line=0
for /f "delims=" %%l in (%inputfile%) do (
set /a line+=1
if !line!==4 (
echo WORDS YOU REPLACE IT WITH>>%tempfile%
) else (
echo %%l>>%tempfile%
)
)
del %inputfile%
ren %tempfile% %inputfile%
endlocal
Another option might be to overwrite the file entirely. Here's the part to do that:
:saveVars
(
ECHO %v1%
ECHO %v2%
ECHO %v3%
ECHO %v4%
ECHO %v5%
) >variables.txt
GOTO :EOF
That is, if the number of lines is fixed and known beforehand. If not, you might want to store the last value of the increment in your example code and, when saving the variables, use it like this:
:saveVars
SETLOCAL EnableDelayedExpansion
(
FOR /L %%i IN (1,1,%N%) DO (ECHO !v%%i!)
) >variables.txt
ENDLOCAL
GOTO :EOF
I'm assuming here that the v1/v2 etc. variables would be used only for synchronising with the file: when it is read, the lines are stored in those variables, and when any of them (variables) gets changed, you just call the saveVars subroutine immediately. Here's an example how you would use it:
…
SET v2=something
CALL :saveVars
…
SET v4=%somevar%
CALL :saveVars
…
If the file is small, the rewriting should be fast enough.
Not absolutely sure I've understood everything correctly, but if you want to substitute something for an existing part of a text file with a batch script, you'll need to write everything (including the changed part) to a new file first, then delete the original and rename the new file to the original name.
I can't really see a point of reading everything into variables, unless I'm missing something. You could simply iterate over the lines writing them one by one into the new file and replacing the specific line's contents with the substitute text along the way:
setLocal EnableDelayedExpansion
>newFile.txt (
for /f "tokens=* delims= " %%a in (variables.txt) do (
set /a N+=1
if !N! == 4 (ECHO substitute text) ELSE ECHO %%a
)
)
del variables.txt
rename newFile.txt variables.txt
If the substitute text must, in turn, be derived from one of the lines, you could do something like this:
setLocal EnableDelayedExpansion
>newFile.txt (
for /f "tokens=* delims= " %%a in (variables.txt) do (
set /a N+=1
if !N! == 1 SET subst_text=%%a
if !N! == 4 (ECHO !subst_text!) ELSE ECHO %%a
)
)
del variables.txt
rename newFile.txt variables.txt
okay so I have a dir with some files. I want to do a specific file-renamingscript
i'm stuck with this part, taking only the first letter of each part of the filename:
if the filename would be
This.is.a.FIle.mp3
I would like to rename it to
tiaf.mp3
notice i want it to be all in lowercase.
The word length is variable so i cant take reference from it as a local variable !variable:~0,2!
anyone could help?
thanx!
edit: i forggot to ask. If you have an idea to make a test if the filename is of the format i mentioned. Because if the file is called. 'file.mp3' then i wouldn't want it to be renamed to 'f.mp3'
This should work, but if you want to allow also "!" exclamation marks in your filenames, it have to be a little bit extended.
#echo off
setlocal EnableDelayedExpansion
for %%f in ("C:\temp\folder\*.*") do (
call :createName "%%~f"
)
goto :eof
:: Compress a filename with more than one dot to only the first (lower) letters of each part
:: one.TWO.three.four.exe to ottf.exe
:createName <dot-filename>
setlocal
set "filename=#.%~n1"
set "ext=%~x1"
set "count=0"
set "short="
:createName.loop
for %%a in ("!filename!") do (
set "part=%%~xa"
set "filename=%%~na"
if defined part (
set /a count+=1
set "char=!part:~1,1!"
call :toLower char
set "short=!char!!short!"
) ELSE (
set "char="
)
rem echo "%%~na"-"%%~xa" "!char!" "!short!"
)
if defined part goto :createName.loop
set "short=!short!!ext!"
if !count! GTR 1 (
echo ren "%~f1" "!short!"
)
(
endlocal
goto :eof
)
:: convert a char to the lower variant or leave it unchanged if it isn't a char
:: use the %var:*n=% syntax to remove the front of a string, to get the correct char
:toLower <variable to char>
setlocal EnableDelayedExpansion
(
set "char=!%~1!"
set "helper=##aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"
set "lower=!helper:*%char%=!"
set "lower=!lower:~0,1!"
if "!lower!"=="#" set "lower=!char!"
)
(
endlocal
set "%~1=%lower%"
goto :eof
)
Would this kind of logic work for you:
#echo off
for /f "delims=|" %%f in ('dir /b C:\temp') do call :runsub %%f
goto EOF
:runsub
for /f "tokens=1,2,3,4 delims=." %%a in ("%~n1") do set a=%%a&set b=%%b&set c=%%c&set d=%%d
if not "%a%"=="" echo %a%
if not "%b%"=="" echo %b%
if not "%c%"=="" echo %c%
if not "%d%"=="" echo %d%
:EOF
You can change the echo %a%, echo %b%, etc. to sets and get the substring from these. This also only gets the first 4 splits, you can add more if you need. Also change C:\temp to the appropriate directory.