I have file whose format like this:
tcpport 1500
webports 1500,1500
tcpclientport 1500
I want to replace 1500 by 1510, only
when I find the string webports.
I found many scripts on searching and replacing strings, but they replace all the occurrences of a string by another string. That's not what I want.
How can I do that in a Windows batch file?
Here is an answer
================================
#::-----------------------------------------------------------------------------------
#:: --- Fonction substitute_str
#::-----------------------------------------------------------------------------------
:substitute_str
#echo off
setlocal enableextensions disabledelayedexpansion
set "oldstring=%1" rem string to replace
set "newstring=%2" rem replacing string
set "textFile=%3" rem file where replacement will take place
set "texttosearch=%4" rem string to be present in the line so that replacement could take place.
set "FICWORK=C:\temp\EvoSvgWin.wrk"
set "FICTEMP=C:\temp\EvoSvgWin.out"
for /f "delims=" %%i in ('type %textFile% ^& break ^> %textFile% ') do (
set "line=%%i"
type NUL > %FICTEMP%
echo(!line! > %FICWORK%
type "%FICWORK%" | findstr /I "%texttosearch%" > %FICTEMP%
for %%S in ("%FICTEMP%") do ( #set "size=%%~zS" )
if NOT !size! EQU 0 (
echo(!line:%oldstring%=%newstring%! >> %textFile%
) else (
echo(!line! >> %textFile%
)
endlocal
)
del %FICTEMP% %FICWORK%
EXIT /B 0
==========================================================================
NB : >> %textFile% is necessary because the function can be called many times with the same file so that all replacements could take place in the same.
Related
My batch file:
#ECHO off
(FOR /f "delims=" %%i in (source.txt) DO (
SET "line=%%i"
setlocal enabledelayedexpansion
SET "line=!line:Ć=F!"
SET "line=!line:Ç=G!"
SET "line=!line:Ň=R!"
SET "line=!line:Ô=T!"
ECHO.!line!
endlocal
))>"output.txt"
My source.txt file:
ĆÇŇÔ
Expected output.txt file:
FGRT
Current output.txt file:
FFRR
My question is: what's wrong here?
If source.txt is not saved as Unicode, your issue may be related to the codepage at the time you run your loop.
The following example switches to codepage 1252, West European Latin, (as also suggested in the comments by Gerhard), if not that already. Although I'd assume codepage 850, Multilingual (Latin I) should work equally well. (Just change to the codepage required by replacing 1252 on lines 7, and 8, as necessary).
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
If Not Exist "source.txt" GoTo :EOF
For /F "Delims==" %%G In ('2^> NUL Set _cp') Do Set "%%G="
For /F Tokens^=* %%G In ('"%SystemRoot%\System32\chcp.com"'
) Do For %%H In (%%G) Do Set "_cp=%%~nH"
If Not %_cp% Equ 1252 (Set "_cpc=TRUE"
"%SystemRoot%\System32\chcp.com" 1252 1> NUL)
(For /F UseBackQ^ Delims^=^ EOL^= %%G In ("source.txt") Do (
Set "line=%%G"
SetLocal EnableDelayedExpansion
Set "line=!line:Ć=F!"
Set "line=!line:Ç=G!"
Set "line=!line:Ň=R!"
Set "line=!line:Ô=T!"
Echo=!line!
EndLocal)) 1> "output.txt"
If Defined _cpc "%SystemRoot%\System32\chcp.com" %_cp% 1> NUL
Please note that using a For loop like this, will remove any blank lines from the output
The answer (as suggested by #Gerhard and #Compo): it was the wrong code page.
Below is my current working batch code if someone else will ever be in the same need (to convert inverted ATASCII characters in ATARI BASIC code).
It converts defined set of characters (you can add more / delete some - just modify strings and the total number of characters) and make comments more visible by adding lines at the start and at the end of each one.
#ECHO off
rem --------------------------------------------------
rem CHECK FOR THE SOURCE FILE
rem --------------------------------------------------
IF "%~1"=="" GOTO :End
rem --------------------------------------------------
rem SET THE CODE PAGE
rem --------------------------------------------------
CHCP 1252 > NUL
rem --------------------------------------------------
rem DEFINE THE SET OF CHARACTERS TO CONVERT
rem --------------------------------------------------
SET "input_set_of_chars= Ł¤§¨cŞ«¬R°+˛ł´u¸ą»Ľ˝ľÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚř"
SET "output_set_of_chars= #$'()*+,-.0123456789;<=>ABCDEFGHIJKLMNOPQRSTUVWXYZx"
SET "number_of_chars=52"
rem --------------------------------------------------
rem CONVERT EACH LINE OF THE SOURCE FILE
rem --------------------------------------------------
(FOR /f "delims=" %%i in (%~1) DO (
SET "line=%%i"
CALL :ConvertASCII
))> "%~n1-converted%~x1"
GOTO :End
rem --------------------------------------------------
rem START OF THE CONVERT SUBROUTINE
rem --------------------------------------------------
:ConvertASCII
SETLOCAL enableDelayedExpansion
rem --------------------------------------------------
rem IF THE LINE IS NOT A COMMENT THEN DO NOT CONVERT
rem --------------------------------------------------
IF "!line!"=="!line: REM =!" GOTO :LoopEnd
rem --------------------------------------------------
rem MAKE COMMENT LINE A LITTLE MORE VISIBLE
rem --------------------------------------------------
SET "line=!line: REM = REM ----------!----------"
rem --------------------------------------------------
rem CONVERT ALL DEFINED CHARACTERS
rem --------------------------------------------------
SET "counter=0"
:LoopStart
SET "input_char=!input_set_of_chars:~%counter%,1!"
SET "output_char=!output_set_of_chars:~%counter%,1!"
SET "line=!line:%input_char%=%output_char%!"
SET /a counter+=1
IF "!counter!"=="!number_of_chars!" GOTO :LoopEnd
GOTO :LoopStart
rem --------------------------------------------------
rem ECHO THE CURRENT LINE AND EXIT SUBROUTINE
rem --------------------------------------------------
:LoopEnd
ECHO.!line!
ENDLOCAL
EXIT /b 0
:End
As per your comment, it is actually not really a code page issue, because you have got ATASCII encoding in your *.bas files. For converting such files to avoid inverted glyphs, I would use a language that can easily read the file in binary mode and subtract 0x80 from every byte whose value is greater than or equal to 0x80.
Anyway, if you do want to replace the characters left over from your already performed conversion process (Ć, Ç, Ň, Ô, with codes 0x8F, 0x80, 0xD5, 0xE2, resp., as per your active code page 852), I would do it the following way, applying code page 437 during any conversion activities, because this defines the character set of the original IBM PC, also known as OEM font, where there should not occur any unwanted character conversion in the background:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (full path to target directory)
set "_SOURCE=source.txt" & rem // (name of source file)
set "_RETURN=return.txt" & rem // (name of return file)
set "_FILTER=^[0-9][0-9]* *REM " & rem /* (`findstr` search expression to filter
rem for specific lines; `^` means all) */
rem // Store current code page:
for /F "tokens=2 delims=:" %%P in ('chcp') do for /F %%C in ("%%P") do set "$CP=%%C"
rem // Set code page to OEM in order to avoid unwanted character conversions:
> nul chcp 437
rem /* Specify character replacements; the `forfiles` command supports substitution
rem of hex codes like `0xHH`, so you can specify special characters by their code
rem in order to avoid having to embed them into this script, which might in turn
rem lead to problems due to dependencies on the current code page; each `0x22`
rem represents a `"` to enclose each replacement expression within quotes; each
rem of the following replacement expression is `"` + char. + `=` + char. +`"`: */
for /F "delims=" %%R in ('
forfiles /P "%~dp0." /M "%~nx0" /C ^
"cmd /C echo 0x220x8F=F0x22 0x220x80=G0x22 0x220xD5=R0x22 0x220xE2=T0x22"
') do set "RPL=%%R"
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Write into return file:
> "%_RETURN%" (
rem /* Read from source file, temporarily precede each line with line number
rem followed by a `:` in order to not lose blank lines: */
for /F "delims=" %%L in ('findstr /N "^" "%_SOURCE%"') do (
rem // Store current line:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Remove temporary line number prefix:
set "LINE=!LINE:*:=!"
rem // Filter for lines that are subject to the replacements:
cmd /V /C echo(^^!LINE^^!| > nul findstr /R /I /C:"!_FILTER!" && (
rem // Perform replacements one after another:
for %%R in (%RPL%) do if defined LINE set "LINE=!LINE:%%~R!"
)
rem // Return resulting line:
echo(!LINE!
endlocal
)
)
rem // Return from target directory:
popd
)
rem // Restore former code page:
if defined $CP > nul chcp %$CP%
endlocal
exit /B
This approach performs the character replacements only in lines that begin with: a decimal number, followed by one or more SPACEs, followed by REM in a case-insensitive manner, followed by a SPACE.
Here is now a script that truly converts ATASCII characters in REM comments in your Atari Basic (*.bas) file, using certutil for converting the binary character codes:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_TARGET=%~dp0." & rem // (full path to target directory)
set "_SOURCE=source.txt" & rem // (name of source file)
set "_RETURN=return.txt" & rem // (name of return file)
set "_FILTER=^[0-9][0-9]* *REM " & rem /* (`findstr` search expression to filter
rem for specific lines; `^` means all) */
set "_TEMPFN=%TEMP%\%~n0_%RANDOM%" & rem // (path and base name of temporary file)
rem // Change into target directory:
pushd "%_TARGET%" && (
rem // Write into return file:
> "%_RETURN%" (
rem /* Read from source file, temporarily precede each line with line number
rem followed by a `:` in order to not lose blank lines: */
for /F "delims=" %%L in ('findstr /N "^" "%_SOURCE%"') do (
rem // Store current line:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Remove temporary line number prefix:
set "LINE=!LINE:*:=!"
rem // Filter for lines that are subject to the replacements:
cmd /V /C echo(^^!LINE^^!| > nul findstr /R /I /C:"!_FILTER!" && (
rem // Found a line, hence write it to temporary file:
(> "!_TEMPFN!.tmp" echo(!LINE!) && (
rem // Convert temporary file to hex dump file:
> nul certutil -f -encodehex "!_TEMPFN!.tmp" "!_TEMPFN!.cnv" 4 && (
rem // Write to temporary file:
(> "!_TEMPFN!.tmp" (
rem // Read hex dump file line by line:
for /F "usebackq tokens=*" %%T in ("!_TEMPFN!.cnv") do (
rem // Reset buffer, loop through hex values:
set "BUFF= " & for %%H in (%%T) do (
rem // Determine new hex value, append it to buffer:
set "HEX=%%H" & set /A "FIG=0x!HEX:~,1!-0x8"
if !FIG! lss 0 (
rem // Value was < 0x80, hence keep it:
set "BUFF=!BUFF! !HEX!"
) else (
rem // Value was >= 0x80, hence subtract 0x80:
set "BUFF=!BUFF! !FIG!!HEX:~1!"
)
)
echo(!BUFF:~2!
)
)) && (
> nul certutil -f -decodehex "!_TEMPFN!.tmp" "!_TEMPFN!.cnv" 4 && (
type "!_TEMPFN!.cnv"
) || echo(!LINE!
) || echo(!LINE!
) || echo(!LINE!
) || echo(!LINE!
) || (
rem // Return resulting line:
echo(!LINE!
)
endlocal
)
)
rem // Clean up temporary files:
del "%_TEMPFN%.tmp" "%_TEMPFN%.cnv"
rem // Return from target directory:
popd
)
endlocal
exit /B
So I got VERY close to what I wanted to achieve, but I couldn't seem to get past the points I am currently stuck on.
First off, I have a .ini file that has a header along with a list of items separated by a comma:
[Items]
ListOfItems = Item01, Item02, Item03
What I need to do is find that specific section (ListOfItems) and append a new item to it that doesn't exist in a reference file.
Here's what I have so far:
SET /P NewItem=
SET ListOfItems=
FOR /F delims^=^"^ tokens^=2 %%G IN ('type File.ini') DO (set ListOfItems=%%G)
echo ListOfItems ^=^"%ListOfItems%, %NewItem%^">File.ini
The issue I have is that I am using a prompt instead of taking a new line from a file (List.txt) because the last time I tried it kept repeating the same line over and over, instead of only taking one of each line, provided it isn't a duplicate.
The other issue I have is that, with the current code above, it doesn't preserve the current state of the .ini file. So you can have the example as per above, but once you add a new item via the prompt you end up with:
ListOfItems =", Item04"
The header is gone, the original values are gone, and it starts with a comma, instead of only adding a comma after each value.
So the expected result was:
[Items]
ListOfItems = Item01, Item02, Item03, Item04
Why is it not preserving the original data when first run, but subsequent runs perfectly copies the original data and adds to it?
How can we address the issue of it starting with a comma, with no previous data in front of it?
How can we have it pull the list of items to append from List.txt instead of manually entering them one at a time?
EDIT: I've managed to address the 2nd issue with the following:
(
ECHO [Items]
ECHO !ArchiveList! = !RequiredItems!
)>>File.ini
Where !RequiredItems! contain the base items Item01, Item02, Item03 which I do when the ListOfItems are not populated, as per John Kens' suggestion.
As for the 3rd issue, I solved it with:
FOR /F "delims=" %%A IN (List.txt) DO (
SET "ListAddition=!ListAddition!%%A, "
)
SET "ListAddition=!ListAddition:~0,-2!"
Which takes each item in the List.txt file, which is on a new line, and then adds it to a list, each item separated by a comma.
Alright first things first, your current for loop needs some tweaking; There are a few things we can do here. If we just wanted to grab what ListOfItems equals from the seconened line and ignore the fact there may be other objects in this file, then the proper way to grab this data will the the following:
FOR /F "skip=1 tokens=2,*" %%A IN ('type File.ini') DO (set "ListOfItems=%%B")
skip=1 - Will skip the first line
tokens=2,* - Having 2,* will cause %%A to be first two objects, and %%B will be everything after 2.
To be more proper however, the correct way will be to use a find /i statment to look for the ListOfItems object within the .ini file. Lets take the following text file bellow:
[Items]
ListOfItems = Item01, Item02, Item03
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water
If we used that basic for loop on this statement we would get the last line of the text file:
Sword, Dagger, Holy Water
Bellow is using the find statement along with another loop we can combine them to only extract data after ListOfItems from the whole document.
Rem | Get .ini To String
FOR /F "tokens=*" %%A IN ('type File.ini') DO (
Rem | Look For Line With Items "ListOfItems"
FOR /F "tokens=2,*" %%B IN ('echo %%A^| find /i "ListOfItems"') DO (
Rem | Echo Result
echo %%C
)
)
However, for correcting only that one line and adding a new object to the end, this is where it gets tricky! Keep in mind that batch is primitive and loosing support, it's limitation's are well, limited compared to its successor, powershell. In raw batch there is not a true command for editing just one line in the middle of a document. However, this does not make it impossible.
To get around this we will have to take the entire .ini file and use the type command to break down the document line by line and save it as a string. From there we can use syntax-replace to edit the "String" and save it to a new document. From there we just delete and rename.
To further expand on this we will need to check if ListOfItems is actually populated. The basic if exists statement will work great here. Now because your statement has an = in the equation, simple syntax-replace will not work without further complications. From my previous edit, I changed the simple function to a script that we will call too. This script will be called Replace.bat. All you need to do is make a new .bat file and paste it in from bellow. This file will never need modified.
Bellow is the entire project that should solve all your issues:
Replace.Bat:
(This entire script is the equivalent of a single 15 character command in powershell lol!)
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FILE_I=%~1"
set "SEARCH=%~2"
set "REPLAC=%~3"
set "FILE_O=%~4"
set "FLAG=%~5"
if not defined FILE_I exit /B 1
if not defined SEARCH exit /B 1
if not defined FILE_O set "FILE_O=con"
if defined FLAG set "FLAG=#"
for /F "delims=" %%L in ('
findstr /N /R "^" "%FILE_I%" ^& break ^> "%FILE_O%"
') do (
set "STRING=%%L"
setlocal EnableDelayedExpansion
set "STRING=!STRING:*:=!"
call :REPL RETURN STRING SEARCH REPLAC %FLAG%
>> "%FILE_O%" echo(!RETURN!
endlocal
)
endlocal
exit /B
:REPL rtn_string ref_string ref_search ref_replac flag
setlocal EnableDelayedExpansion
set "STR=!%~2!"
set "SCH=!%~3!"
set "RPL=!%~4!"
if not defined SCH endlocal & set "%~1=" & exit /B 1
set "SCH_CHR=!SCH:~,1!"
if not "%~5"=="" set "SCH_CHR="
if "!SCH_CHR!"=="=" set "SCH_CHR=" & rem = terminates search string
if "!SCH_CHR!"==""^" set "SCH_CHR=" & rem " could derange syntax
if "!SCH_CHR!"=="%%" set "SCH_CHR=" & rem % ends variable expansion
if "!SCH_CHR!"=="^!" set "SCH_CHR=" & rem ! ends variable expansion
call :LEN SCH_LEN SCH
call :LEN RPL_LEN RPL
set /A RED_LEN=SCH_LEN-1
set "RES="
:LOOP
call :LEN STR_LEN STR
if not defined STR goto :END
if defined SCH_CHR (
set "WRK=!STR:*%SCH_CHR%=!"
if "!WRK!"=="!STR!" (
set "RES=!RES!!STR!"
set "STR="
) else (
call :LEN WRK_LEN WRK
set /A DFF_LEN=STR_LEN-WRK_LEN-1,INC_LEN=DFF_LEN+1,MOR_LEN=DFF_LEN+SCH_LEN
for /F "tokens=1,2,3 delims=," %%M in ("!DFF_LEN!,!INC_LEN!,!MOR_LEN!") do (
rem set "RES=!RES!!STR:~,%%M!"
if defined WRK set "WRK=!WRK:~,%RED_LEN%!"
if "!STR:~%%M,1!!WRK!"=="!SCH!" (
set "RES=!RES!!STR:~,%%M!!RPL!"
set "STR=!STR:~%%O!"
) else (
set "RES=!RES!!STR:~,%%N!"
set "STR=!STR:~%%N!"
)
)
)
) else (
if "!STR:~,%SCH_LEN%!"=="!SCH!" (
set "RES=!RES!!RPL!"
set "STR=!STR:~%SCH_LEN%!"
) else (
set "RES=!RES!!STR:~,1!"
set "STR=!STR:~1!"
)
)
goto :LOOP
:END
if defined RES (
for /F delims^=^ eol^= %%S in ("!RES!") do (
endlocal
set "%~1=%%S"
)
) else endlocal & set "%~1="
exit /B
:LEN rtn_length ref_string
setlocal EnableDelayedExpansion
set "STR=!%~2!"
if not defined STR (set /A LEN=0) else (set /A LEN=1)
for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if defined STR (
set "INT=!STR:~%%L!"
if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!"
)
)
endlocal & set "%~1=%LEN%"
exit /B
Main.Bat:
#ECHO OFF
#setlocal EnableDelayedExpansion
Rem | Configuration
set "CustomINI=File.ini"
set "ListHeader=[Items]"
set "Object=ListOfItems"
set "ReplaceScript=Replace.bat"
SET "ItemList=List.txt"
Rem | Check If "CustomINI" Exists
if not exist "%CustomINI%" (
echo File "%CustomINI%" Not Found!
pause
goto :EOF
)
Rem | Check If "ItemList" Exists
if not exist "%ItemList%" (
echo File "%ItemList%" Not Found!
pause
goto :EOF
)
goto StartFunction
:StartFunction
Rem | Generate the list of items from textfile
FOR /F "delims=" %%A IN (%ItemList%) DO (
set "ListAddition=!ListAddition!%%A, "
)
set "ListAddition=!ListAddition:~0,-2!"
Rem | Get .ini To String
set HeaderFound=false
FOR /F "tokens=*" %%A IN ('type !CustomINI!') DO (
Rem | First Find The Header "[Items]" & Extract "ListOfItems" Line Data
for /f "tokens=*" %%B in ('echo %%A') do (
set "item=%%B"
if /i "!item!"=="!ListHeader!" (
set HeaderFound=true
) else if not "!item!"=="!item:ListOfItems=!" if "!HeaderFound!"=="true" (
Rem | Turn Items For Line "ListOfItems" To String
for /f "tokens=2,*" %%C in ('echo %%B') do (
Rem | Set String
set "SEARCHTEXT=%%D"
)
set HeaderFound=false
)
)
)
Rem | Check If "ListOfItems" Is Actually Populated
If "%SEARCHTEXT%"=="" (
Rem | Not Populated
set "SEARCHTEXT=!Object! = "
set "REPLACETEXT=!Object! = !ListAddition!"
goto EditString
) ELSE (
Rem | Populated
set "REPLACETEXT=!SEARCHTEXT!, !ListAddition!"
goto EditString
)
:EditString
Rem | Edit Only "ListOfItems" Line
Rem | Usage: call "1" "2" "3" "4" "5"
Rem | call - Calls external script
Rem | "1" - Name of External script
Rem | "2" - File to Edit
Rem | "3" - Text to replace ex: red apple
Rem | "4" - Text to replace to ex: green apple
Rem | "5" - Output file
call "%ReplaceScript%" "%CustomINI%" "%SEARCHTEXT%" "%REPLACETEXT%" "%CustomINI%.TEMP"
Rem | Delete Original File, Restore New
del "%CustomINI%"
rename "%CustomINI%.TEMP" "%CustomINI%"
goto :EOF
PS - Keep note of the following: The above script expects that when ListOfItems = is not populated, it has a space after the =. If this is not how it is in your .ini file then change set "SEARCHTEXT=!OBJECT! = " to set "SEARCHTEXT=!OBJECT! =" from in the for statement.
EDIT: Since recent requests, The following was updated:
Firstly, since I was unsure of the OP's meaning of ListOfItems = being "Blank", I assumed that he/she was referring to it being ListOfItems =. - Not it being actually missing from the ListHeader it's self. In the example bellow.
My Vision - File.ini:
[Items]
ListOfItems =
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water
OP's Vision - File.ini
[Items]
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water
Since then, I have now updated the script to find [Items] (String) then add a new line under it. This was done using a script by Magoo.
Since there is nothing to replace, we simply can just "Add" onto the the .ini thus we call a different function.
:EditMissingString
Rem | Export SearchString
echo !ListHeader!>> %~dp0ListHeader.txt
Rem | Add Text Under %ListHeader%
(
FOR /f "delims=" %%i IN (ListHeader.txt) DO (
SET AddAfter=%%i
FOR /f "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
SET line=%%n
SET line=!line:*:=!
ECHO(!line!
IF "!line!"=="!AddAfter!" ECHO(%AddTEXT%
)
)
)>>%CustomINI%.TEMP
Rem | Remove ListHeader.txt
DEL %~dp0ListHeader.txt
Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%
goto :EOF
Being that we are no longer editing ListOfArmor = alone but rather adding onto it, we no longer will need the Replace.bat script. I also fixed the original script to properly reserve empty lines!
New replace function W/H Line preserve.
:EditExistingString
REM | Make sure we only edit the ListOfItems line.
FOR /F "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
SET line=%%n
SET Modified=!line:%SearchText%=%ReplaceText%!
SET Modified=!Modified:*:=!
REM | Output the entire edited INI to a temporary file.
>> %CustomINI%.TEMP ECHO(!Modified!
)
Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%
goto :EOF
Result In:
[Items]
ListOfItems = Item1, Item2, Item3
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water
Result Out:
[Items]
ListOfItems = Item1, Item2, Item3, Item4, Item5, Item6
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water
Final Batch Script:
#ECHO OFF
#setlocal EnableDelayedExpansion
Rem | Configuration
set "CustomINI=File.ini"
set "ListHeader=[Items]"
set "Object=ListOfItems"
SET "ItemList=List.txt"
Rem | Check If "CustomINI" Exists
if not exist "%CustomINI%" (
echo File "%CustomINI%" Not Found!
pause
goto :EOF
)
Rem | Check If "ItemList" Exists
if not exist "%ItemList%" (
echo File "%ItemList%" Not Found!
pause
goto :EOF
)
goto StartFunction
:StartFunction
Rem | Generate the list of items from textfile
FOR /F "delims=" %%A IN (%ItemList%) DO (
set "ListAddition=!ListAddition!%%A, "
)
if "%ListAddition%"=="" (
echo ERROR: File "%ItemList%" Is Empty!
pause
goto :EOF
) ELSE (set "ListAddition=!ListAddition:~0,-2!")
Rem | Get .ini To String
set HeaderFound=false
FOR /F "tokens=*" %%A IN ('type !CustomINI!') DO (
Rem | First Find The Header "[Items]" & Extract "ListOfItems" Line Data
for /f "tokens=*" %%B in ('echo %%A') do (
set "item=%%B"
if /i "!item!"=="!ListHeader!" (
set HeaderFound=true
) else if "!HeaderFound!"=="true" (
Rem | Turn Items For Line "ListOfItems" To String
for /f "tokens=2,*" %%C in ('echo %%B') do (
Rem | Set String
set "SearchText=%%D"
)
Rem | Header Was Found, End Loop & goto HeaderContinue
set HeaderFound=false
goto HeaderContinue
)
)
)
Rem | Header Was Not Found
echo ERROR: The Header "%ListHeader%" Was Not Found!
pause
goto :EOF
:HeaderContinue
Rem | Check If "ListOfItems" Is Actually Populated
If "%SearchText%"=="" (
Rem | Not Populated
set "SearchText=!ListHeader!"
set "AddTEXT=!Object! = !ListAddition!"
goto EditMissingString
) ELSE (
Rem | Populated
set "REPLACETEXT=!SearchText!, !ListAddition!"
goto EditExistingString
)
:EditExistingString
REM | Make sure we only edit the ListOfItems line.
FOR /F "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
SET line=%%n
SET Modified=!line:%SearchText%=%ReplaceText%!
SET Modified=!Modified:*:=!
REM | Output the entire edited INI to a temporary file.
>> %CustomINI%.TEMP ECHO(!Modified!
)
Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%
goto :EOF
:EditMissingString
Rem | Add Text Under %ListHeader%
(
FOR /f "delims=" %%i IN ('Echo !ListHeader!') DO (
SET AddAfter=%%i
FOR /f "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
SET line=%%n
SET line=!line:*:=!
ECHO(!line!
IF "!line!"=="!AddAfter!" ECHO(%AddTEXT%
)
)
)>>%CustomINI%.TEMP
Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%
goto :EOF
PS: I know your find command is in a different location or something just change the command find to %WINDIR%\System32\FIND.exe in the script.
DEBUG/CHANGES:
Scraped.
For help on any of the commands do the following:
call /?
set /?
for /?
if /?
find /?
So on.
I have a text file that contains some information and looks like this:
7253 198760.294 533963.581
7373 198752.213 533954.046
739CT 198751.288 533952.902
In every line there are parameters that has spaces between them but the spaces (not tabs) are for ease or reading only.
I need it to look like this:
1550,168040.682,630305.751,
1575,168023.241,630287.837,
15964TS,168008.317,630272.508,
Here is my code:
#echo off
setlocal EnableDelayedExpansion
set LF=^
set "content="
for /f "delims=" %%x in (input.txt) do (
set "content=!content!%%x!LF!"
)
:loop
if defined content (
set "new=!content: = !"
if "!new!" neq "!content!" (
set "content=!new!"
goto :loop
)
)
if defined content if "!str:~0,1!" equ " " set "content=!str:~1!"
if defined content if "!str:~-1!" equ " " set "content=!str:~0,-1!"
echo(!content!
set string=!content! & echo !string: =,! > output.txt
endlocal
pause > null
It turns everything in one line and connects between everything without spaces.
Concatenation to a single string is dangerous, because of limited max string length. Better process each line on it's own:
(for /f "tokens=1-3" %%a in (infile.txt) do (
echo %%a,%%b,%%c,
))>outfile.txt
note: empty lines will be ignored (will get lost)
I have a folder with 300+ text files; I am trying to create a batch script that will find anything after a certain date with the following lines within each text:
---------- \SC####SVR####\E$\USERS\SC####POS####\E2ELOGS\PED_20141116_110913.DBG: 1
As indicated date format would be YYYYMMDD
For example:
set filedatetime=10/11/2014 12:26
set filedatetime=%filedatetime:~6,4%%filedatetime:~3,2%%filedatetime:~0,2%
echo "%filedatetime%"
FINDSTR "%FILEDATETIME%" C:\RESULTS\*.TXT
And if the findstr result is GTR than 20141110 echo the line out to another txt file.
If the date portion of the string is always in the same location, you can simply use the string handling functions to parse it out. At that point, convert it to an integer if necessary, then compare the result with your target date.
#ECHO OFF
SETLOCAL
SET filedatetime=20141116
SET "sourcedir=U:\sourcedir\t w o"
(
FOR /f "tokens=1-3delims=_" %%a IN ('findstr /r ".*_.*_.*" "%sourcedir%\*.txt"') DO (
IF %%b geq %filedatetime% ECHO %%a_%%b_%%c
)
)>newfile.txt
TYPE newfile.txt
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
Produces a new file newfile.txt
I used a fixed date for convenience of testing.
geq produces lines equal to or greater than the date selected. gtr would yield strictly greater than.
Here's my test data (I've abbreviated it)
-S\PED_20140129_110913.DBG: 1
-S\PED_20140229_110913.DBG: 1
-S\PED_20140329_110913.DBG: 1
-S\PED_20140429_110913.DBG: 1
-S\PED_20140529_110913.DBG: 1
-S\PED_20140629_110913.DBG: 1
-S\PED_20140729_110913.DBG: 1
-S\PED_20140829_110913.DBG: 1
-S\PED_20140929_110913.DBG: 1
-S\PED_20141029_110913.DBG: 1
-S\PED_20141129_110913.DBG: 1
-S\PED_20141229_110913.DBG: 1
And results:
U:\sourcedir\t w o\extra.txt:-S\PED_20141129_110913.DBG: 1
U:\sourcedir\t w o\extra.txt:-S\PED_20141229_110913.DBG: 1
I would suggest that the problem is in the filenames section - if underscores appear there, then that an incorrect string would be taken for comparison.
You could test this with (replacement if statement)
IF %%b geq %filedatetime% ECHO "%%b" geq "%filedatetime%"
which would show which strings are being compared.
This should fix that problem:
#ECHO OFF
SETLOCAL
SET filedatetime=20141116
SET "sourcedir=U:\sourcedir\t w o"
(
FOR /f "tokens=1,2,*delims=:" %%p IN ('findstr /r ".*_.*_.*" "%sourcedir%\*.txt"') DO (
FOR /f "tokens=1-3delims=_" %%a IN ("%%r") DO (
IF %%b geq %filedatetime% ECHO %%p:%%q:%%a_%%b_%%c
)
)
)>newfile.txt
TYPE newfile.txt
GOTO :EOF
which separates-out the filename from the data, then processes the data alone.
#echo off
setlocal
set /a filedatetime=20141124
set sourcedir=
set outfile=E:\outfile.txt
REM There are 3 underscores per record in the input files, creating 4 tokens. The third token (%%c) is the date in the filename.
REM Due to the presence of at least one backup file named "BACK_*", which adds a fourth underscore, we filter that out.
for /f "tokens=1-4 delims=_" %%a in ('findstr /r ".*_.*_.*" "%sourcedir%\*.txt"') DO (
if %%c geq %filedatetime% echo %%a_%%b_%%c_%%d | find /I /V "BACK" >>%outfile%
)
rem type newfile.txt
goto :EOF
exit
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.