Script stores the lines along with line numbers where ever "firstword" is found, those line numbers are retrieved from temp_onsetTime.txt. trying to retrieve the 4th line from that line number and write it in another file.
line set var=%%g% set /a var=%var% + 4 , string var is not getting converted into integer.
cls
break>ontset_time.txt
break>temp_ontsetTime.txt
set firstword="stimuli\1200.wav"
set secondword="MainScreen.OnsetTime"
findstr /l /I /n %firstword% "C:\Users\BHAVYA\Desktop\AudOdd.txt">>temp_ontsetTime.txt
for /F " delims=: tokens=1 " %%g in ( C:\Users\BHAVYA\Desktop\temp_ontsetTime.txt ) do (
echo %%g
echo.
set var=%%g%
set /a var=%var% + 4
echo %var%
for /F " delims=: tokens=1,2 skip= %var%" %%h IN ( ' findstr /l /I %secondword% "C:\Users\BHAVYA\Desktop\AudOdd.txt" ' ) do (
echo %%h %%i
echo %%i%>>ontset_time.txt
goto loop_end
)
:loop_end
echo loop ended
)
pause
Problem 1: set var=%%g% is incorrect; set var=%%g is closer (%%g accesses the metavariable %%g); set /a var=%%g is better still (since %%g is apparently an integer).
Problem2: You need to learn about delayed expansion - there are hundreds of references here on SO. Batch replaces every %var% with its value as it stands when the for loop is parsed - not when it is executed, hence
set /a = var + 4
will increase the value of var by 4 - you then need to use
call echo %%var%%
to access the changed value of var if you are not using delayed expansion.
AAMOI, this is equivalent to
set /a var=%%g+4
Third problem - still the delayedexpansion problem - %var% will be replaced by its value at the time for...%%g... was parsed, not the value as modified within the loop.
Fourth problem : labels are not allowed within a block statement (a parenthesised series of statements).
Here's a cure without delayed expansion
for /F " delims=: tokens=1 " %%g in ( C:\Users\BHAVYA\Desktop\temp_ontsetTime.txt ) do (
echo %%g
echo.
set /a var=%%g + 4
call echo %%var%%
for /f "tokens=1,2delims==" %%m in ('set var') do if /i "%%m"=="var" (
echo var is %%n
set "show=Y"
for /F " delims=: tokens=1,2 skip=%%n" %%h IN ( ' findstr /l /I %secondword% "C:\Users\BHAVYA\Desktop\AudOdd.txt" ' ) do if defined show (
echo %%h %%i
set "show="
echo %%i%>>ontset_time.txt
)
)
echo loop ended
)
Note here that using set var will list the current environment values that begin var in the format var=99. We tokenise that on = and the first token (%%m) must be var, not var1 or anything else. If it is, its value is in %%n, so set the flag show to a value (any value will do) and then process the file. On the first "hit", clear the value in show- it is now undefined, so no more processing takes place until the next outer loop starts.
[I obviously can't test this]
Related
i have to search a string from a txt like Pippo.K=5 and replace it with Pippo.K=1. I need to search the entire string. What i did is:
set "search=Pippo.K=5"
set "replace=Pippo.K=1"
set "textFile=%SettingFile%.txt"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
but what i returned is
5=Pippo.K=1=5
How can i fix this error?
The following script constitutes a pure batch-file solution. Supposing it is stored as repl-str.bat, you need to call it like this for your application:
repl-str.bat "%SettingFile%.txt" "Pippo.K=5" "Pippo.K=1" "%SettingFile%.txt"
This specifies the input file %SettingFile%.txt, the literal and case-sensitive search string Pippo.K=5, the replacement string Pippo.K=1 and the output file %SettingFile%.txt that is the same as the input file (the related technique has been taken from this answer: Batch script to find and replace a string in text file without creating an extra output file for storing the modified file). If no output file is given, the result is output to the console (useful for testing). If a fifth command line argument is given (arbitrary value), the search is done in a case-sensitive manner.
Here is the code of the script repl-str.bat:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FILE_I=%~1"
set "SEARCH=%~2"
set "REPLAC=%~3"
set "FILE_O=%~4"
set "CASE=%~5"
set "FLAG=%~6"
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 CASE set "CASE=#"
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 "%CASE%" "%FLAG%"
>> "%FILE_O%" echo(!RETURN!
endlocal
)
endlocal
exit /B
:REPL rtn_string ref_string ref_search ref_replac case flag
setlocal EnableDelayedExpansion
set "STR=!%~2!"
set "SCH=!%~3!"
set "RPL=!%~4!"
if "%~5"=="" (set "OPT=/I") else (set "OPT=")
if not defined SCH endlocal & set "%~1=" & exit /B 1
set "SCH_CHR=!SCH:~,1!"
if not "%~6"=="" 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 %OPT% "!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 %OPT% "!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 %OPT% "!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
Basically, this approach takes the first character of the search string and looks it up in the input text. At each match, it is checked whether the whole search string occurs. If so, it is replaced by the replacement string by removing as many characters as the search string consists of, hence avoiding sub-string replacement syntax which fails in case the search string contains =, or the search or the replacement string contains % or !.
However, if the first character of the search string is =, ", % or !, the approach is different, the script checks every single character position for occurrence of the search string then, with the disadvantage of reduced overall performance. If a sixth command line argument is given (arbitrary value), this (slow) mode is forced.
Batch variable substring substitution does have limitations. Dealing with literal equal signs is one of them.
powershell "(gc \"%textFile%\") -replace '%search%','%replace%'"
would work. That PowerShell one-liner is a simple alternative to your for /f loop without that limitation.
If you prefer a for /F loop, if your text file is an ini-style file, try this:
#echo off & setlocal
set "searchItem=Pippo.K"
set "searchVal=5"
set "newVal=1"
set "textFile=test.txt"
>"outfile.txt" (
for /f "eol=; usebackq tokens=1* delims==" %%I in ("%textFile%") do (
if /I "%%~I"=="%searchItem%" (
if "%%~J"=="%searchVal%" (
echo %%I=%newVal%
) else echo %%I=%%J
) else (
if not "%%~J"=="" (echo %%I=%%J) else echo %%I
)
)
)
move /y "outfile.txt" "%textFile%"
Be advised that if any of the items in your file has a blank value (e.g. valuename=), the equal sign will be stripped unless you add some additional logic.
You might also consider using ini.bat from this answer.
I want to create a script that sets specific values, then writes each value into a new line of a text document. After that it should read the document and set new values to a specified line of the text document, then echo those out.
I have tried different values for "skip=#" which didn't change anything. When I tried to not use the "skip=0" option in the first FOR and that makes the batch echo out "Value three" for all values. (Quick edit: I've used this website for information on it so far.)
#ECHO OFF
REM Setting values
SET #valueone=Value one
SET #valuetwo=Value two
SET #valuethree=Value three
REM Saving values
IF EXIST "values.txt" DEL "values.txt"
echo %#valueone% >values.txt
echo %#valuetwo% >>values.txt
echo %#valuethree% >>values.txt
REM Reading values again and echoing them at at the same time.
REM This was separated (first reading then echoing) but it didn't change anything.
FOR /F "skip=0 delims=" %%i IN (values.txt) DO SET #valueonefinal=%%i
echo Value number one:
echo %#valueonefinal%
echo.
FOR /F "skip=1 delims=" %%i IN (values.txt) DO SET #valuetwofinal=%%i
echo Value number two:
echo %#valuetwofinal%
echo.
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET #valuethreefinal=%%i
echo Value number three:
echo %#valuethreefinal%
pause
Expected output in the console:
Value number one:
Value one
Value number two:
Value two
Value number three:
Value three
Actual output:
delims=" was unexpected at this time.
Value number one:
ECHO is off.
Value number two:
Value three
Value number three:
Value three
I'm not that experienced but I suspect that I may be doing the "skip=#" part wrong. Any help with this is greatly apprechiated!
The option skip=0 is not accepted by the for /F command, the specified number must be in the range from 1 to 231 − 1. To skip no lines just do not provide the skip option at all.
You seem to try to assign the text of a certain line to a variable (for instance, the third one):
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET #valuethreefinal=%%i
Well, this actually assigns the content of the last line to the variable, because the set command in the body of the loop is executed for all but the skipped lines. More precisely said, the for /F loop iterates over all non-empty lines which do not begin with ; which is the default character of the eol option.
To actually assign the third line to the variable you need to change the code:
rem // Ensure that the variable is initially unset somewhere before:
set "#valuethreefinal="
rem // As soon as the variable is set the `if` condition is no longer going to be fulfilled:
for /F "usebackq skip=2 delims=" %%i in ("values.txt") do if not defined #valuethreefinal set "#valuethreefinal=%%i"
This does not necessarily assign the third line to the variable, it actually assigns the text of the first line after the (two) skipped ones that is not empty and does not begin with ; (remember the eol character).
The usebackq option allows to put quotation marks around the file name. This is not necessary in your situation, but it is when a file name contains SPACEs or other special characters.
I used the undocumented quoted set syntax here because this is safer than the unquoted one, particularly when it comes to special characters and also to avoid unintended trailing white-spaces.
To disable the eol character you could use the undocumented unquoted option string syntax:
for /F usebackq^ skip^=2^ delims^=^ eol^= %%i in ("values.txt") do if not defined #valuethreefinal set "#valuethreefinal=%%i"
As you can see the SPACEs and =-signs are escaped by the caret symbol ^ in order to treat the whole option string as a unit.
This still skips over empty lines though. To prevent this take a loop at this thread: preserve empty lines in a text file while using batch for /f.
Since you want to capture more than a single line you could extend the code to the following:
set "#valueonefinal=" & set "#valuethreefinal=" & set "#valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined #valueonefinal (
set "#valueonefinal=%%i"
) else (
if not defined #valuetwofinal (
set "#valuetwofinal=%%i"
) else (
if not defined #valuethreefinal (
set "#valuethreefinal=%%i"
)
)
)
)
This can be compressed to:
set "#valueonefinal=" & set "#valuethreefinal=" & set "#valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined #valueonefinal (
set "#valueonefinal=%%i"
) else if not defined #valuetwofinal (
set "#valuetwofinal=%%i"
) else if not defined #valuethreefinal (
set "#valuethreefinal=%%i"
)
)
A more flexible method is to use pseudo-arrays:
rem // Initialise an index counter:
set /A "INDEX=0"
rem // Assign every line to an element of a pseudo-array:
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
rem // Increment the index counter:
set /A "INDEX+=1"
rem // Assign the current line to a pseudo-array element:
call set "#valuefinal[%%INDEX%%]=%%i"
)
The (non-empty) lines of the file value.txt are now assigned to variables called #valuefinal[1], #valuefinal[2], #valuefinal[3], etc. (there is no concept of arrays in batch scripting, the variables are exactly the same as yours, #valueonefinal, etc., that is why I use the term "pseudo").
The call command is used here in order to be able to write and read the variable INDEX within the same block of code; just using set "#valuefinal[%INDEX%]=%%i" would result in assigning and therefore overwriting the variable #valuefinal[0] in every loop iteration.
Your problem is that you are parsing the File from Top to bottom, and skipping the First value, what you don't realize is that FOR will set the value to the LAST item it found. This means that the script as written can only ever return the last item in the values file.
To deal with this you could:
Break the loop on the first match and return that result.
Remove values as they are matched
I like to Break the loop.
First let me make you code a little more streamlined so we can re-write it multiple times to show each
This is going to work exactly as your existing code but now we can easily add more values and loop them in a quick go.
Your Original Code Refactored:
#( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
GOTO :EOF
:Loop
FOR /F "%_ForOptions%" %%i IN (' type "%_ValuesFile%"
') DO ( CALL SET "#value%_Value%final=%%i" )
ECHO.!#value%_Value%final!
GOTO :EOF
* Break the Loop on the First Match:
#( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
ECHO.&ECHO.------ Final Values After %%L Iterations: ------
SET #value
GOTO :EOF
:Loop
FOR /F "Tokens=*" %%A IN ('
CMD /C "FOR /F %_ForOptions% %%i IN (' type "%_ValuesFile%" ') DO #(ECHO.%%i&exit /b)"
') DO #(
SET "#value%_Value%final=%%~A"
)
ECHO.!#value%_Value%final!
GOTO :EOF
Example Output from Break the Loop Version:
Y:\>C:\Admin\S-O_Value-Checker_v2.cmd
------ Iteration: 0 ------
Value number 1:
Value one
------ Iteration: 1 ------
Value number 2:
Value two
------ Iteration: 2 ------
Value number 3:
Value three
------ Final Values After %L Iterations: ------
#value1final=Value one
#value2final=Value two
#value3final=Value three
Press any key to continue . . .
I would like write a batch file to count the number of occurrences of a specific character in each line of a text file.
For example, the count of \ in the string "aa\bb\cc\dd\" would be 4.
The find and the findstr show only the number of lines which is contains the exact character.
You might try the following script, providing the input string as (quoted) command line argument:
set "STRING=%~1$"
set STRING="%STRING:\=" "%"
set /A "COUNT=-1"
for %%E in (%STRING%) do set /A "COUNT+=1"
echo Count of `\`: %COUNT%
This replaces every character to be counted by " + SPACE + " and encloses the entire string in between "", so the input string aa\bb\cc\dd\ becomes "aa" "bb" "cc" "dd" "". The resulting string is fed into a for loop that recognises individual items to iterate through -- five in this case. The counter variable COUNT is initialised with a value of -1, so the result is not the number of iterated items but the separators, namely the \ characters present in the original string.
This approach fails if the string contains ? or * characters. It would also fail in case the character to count is one of the following: ", %, =, *, ~.
#echo off
setlocal
set "string=aa\bb\cc\dd\"
set "count=-1"
for %%a in ("%string:\=" "%") do set /A count+=1
echo %count%
This method works correctly as long as the string don't include wild-card characters: *?; if this is required, I would use the same npocmaka's method, but written in a simpler way:
#echo off
setlocal EnableDelayedExpansion
set "string=aa\bb\cc\dd\"
set "str=A%string%Z"
set "count=-1"
for /F "delims=" %%a in (^"!str:\^=^"^
% Do NOT remove this line %
^"!^") do (
set /A count+=1
)
echo %count%
While slow, you can try with this
#echo off
setlocal enableextensions disabledelayedexpansion
set "inputFile=input.txt"
set "searchChar=\"
for /f "delims=" %%a in ('
findstr /n "^" "%inputFile%"
') do for /f "delims=:" %%b in ("%%~a") do (
set "line=%%a"
for /f %%c in ('
cmd /u /v /e /q /c"(echo(!line:*:=!)"^|find /c "%searchChar%"
') do echo Line %%b has %%c characters
)
The input file is readed using findstr /n to get all the lines in the file with a number prefix (both for output "decoration" and to ensure all the lines in the file are processed). Each line is processed inside a pipe, from cmd to find. The cmd instance is started with unicode output (/u) so when the readed line is echoed, the output will be a two bytes sequence for each input character, one of them a 0x0 ASCII character. The find command sees the 0 as a line terminator, so we get each character in the input line as one separated line. Now, the find command counts in how many lines the searched character happens.
#ECHO OFF
SETLOCAL
SET "String=a\b\c\\\\d"
CALL :count "%string%" \
ECHO %tally%
GOTO :EOF
:count
SETLOCAL enabledelayedexpansion
SET /a tally=0
SET "$2=%~1"
:cloop
SET "$1=%$2%"
SET "$2=!$1:*%2=!"
IF "%$1%" neq "%$2%" SET /a tally+=1&GOTO cloop
endlocal&SET tally=%tally%
GOTO :eof
Here's a way to count particular characters in a string. It won't work for the usual suspects.
here's one way:
#echo off
:checkCountOf string countOf [rtnrVar]
:: checks count of a substring in a string
setlocal EnableDelayedExpansion
set "string=aa"
set "string=%~1"
set "checkCountOf=%~2"
if "%~1" equ "" (
if "%~3" neq "" (
endlocal & (
echo 0
set "%~3=0"
exit /b 0
)
) else (
endlocal & (
echo 0
exit /b 0
)
)
)
if "!checkCountOf!" equ "$" (
set "string=#%string%#"
set "string=!string:%checkCountOf%%checkCountOf%=#%checkCountOf%#%checkCountOf%#!"
) else (
set "string=$%string%$"
set "string=!string:%checkCountOf%%checkCountOf%=$%checkCountOf%$%checkCountOf%$!"
)
set LF=^
rem ** Two empty lines are required
set /a counter=0
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!checkCountOf!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
for /f "tokens=* delims=" %%# in ("!var!") do (
set /a counter=counter+1
)
)
)
if !counter! gtr 0 (
set /a counter=counter-1
)
if "%~3" neq "" (
endlocal & (
echo %counter%
set "%~3=%counter%"
)
) else (
endlocal & (
echo %counter%
)
)
you can call it like:
call ::checkCountOf "/aa/b/c/" "/" slashes
echo %slashes%
exit /b %errorlevel%
wont work with some special characters (",~ and !)
You can also use replacement and the :strlen function
Not tested extensively but works with your example.
#ECHO OFF
SETLOCAL disabledelayedexpansion
SET "String=\a\b\c\\\\d\\"
set "previous=%string%"
set /a count=0
:loop
set "newstg=%previous:*\=%"
IF NOT "%previous%"=="%newstg%" (
set /a count+=1
set "previous=%newstg%"
IF DEFINED previous goto loop
)
echo %count%
pause
GOTO :eof
Here is one more option. I don't think this is bullet proof with poison characters.
#ECHO OFF
SETLOCAL disabledelayedexpansion
SET "String=\\a\b\c\\\\d\\"
set i=0
set "x=%string%"
set "x=%x:\=" & set /A i+=1 & set "x=%"
echo %i%
pause
i have to search a string from a txt like Pippo.K=5 and replace it with Pippo.K=1. I need to search the entire string. What i did is:
set "search=Pippo.K=5"
set "replace=Pippo.K=1"
set "textFile=%SettingFile%.txt"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
but what i returned is
5=Pippo.K=1=5
How can i fix this error?
The following script constitutes a pure batch-file solution. Supposing it is stored as repl-str.bat, you need to call it like this for your application:
repl-str.bat "%SettingFile%.txt" "Pippo.K=5" "Pippo.K=1" "%SettingFile%.txt"
This specifies the input file %SettingFile%.txt, the literal and case-sensitive search string Pippo.K=5, the replacement string Pippo.K=1 and the output file %SettingFile%.txt that is the same as the input file (the related technique has been taken from this answer: Batch script to find and replace a string in text file without creating an extra output file for storing the modified file). If no output file is given, the result is output to the console (useful for testing). If a fifth command line argument is given (arbitrary value), the search is done in a case-sensitive manner.
Here is the code of the script repl-str.bat:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FILE_I=%~1"
set "SEARCH=%~2"
set "REPLAC=%~3"
set "FILE_O=%~4"
set "CASE=%~5"
set "FLAG=%~6"
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 CASE set "CASE=#"
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 "%CASE%" "%FLAG%"
>> "%FILE_O%" echo(!RETURN!
endlocal
)
endlocal
exit /B
:REPL rtn_string ref_string ref_search ref_replac case flag
setlocal EnableDelayedExpansion
set "STR=!%~2!"
set "SCH=!%~3!"
set "RPL=!%~4!"
if "%~5"=="" (set "OPT=/I") else (set "OPT=")
if not defined SCH endlocal & set "%~1=" & exit /B 1
set "SCH_CHR=!SCH:~,1!"
if not "%~6"=="" 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 %OPT% "!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 %OPT% "!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 %OPT% "!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
Basically, this approach takes the first character of the search string and looks it up in the input text. At each match, it is checked whether the whole search string occurs. If so, it is replaced by the replacement string by removing as many characters as the search string consists of, hence avoiding sub-string replacement syntax which fails in case the search string contains =, or the search or the replacement string contains % or !.
However, if the first character of the search string is =, ", % or !, the approach is different, the script checks every single character position for occurrence of the search string then, with the disadvantage of reduced overall performance. If a sixth command line argument is given (arbitrary value), this (slow) mode is forced.
Batch variable substring substitution does have limitations. Dealing with literal equal signs is one of them.
powershell "(gc \"%textFile%\") -replace '%search%','%replace%'"
would work. That PowerShell one-liner is a simple alternative to your for /f loop without that limitation.
If you prefer a for /F loop, if your text file is an ini-style file, try this:
#echo off & setlocal
set "searchItem=Pippo.K"
set "searchVal=5"
set "newVal=1"
set "textFile=test.txt"
>"outfile.txt" (
for /f "eol=; usebackq tokens=1* delims==" %%I in ("%textFile%") do (
if /I "%%~I"=="%searchItem%" (
if "%%~J"=="%searchVal%" (
echo %%I=%newVal%
) else echo %%I=%%J
) else (
if not "%%~J"=="" (echo %%I=%%J) else echo %%I
)
)
)
move /y "outfile.txt" "%textFile%"
Be advised that if any of the items in your file has a blank value (e.g. valuename=), the equal sign will be stripped unless you add some additional logic.
You might also consider using ini.bat from this answer.
I want to read a CSV file line by line and echo something different if the length of the line is 7999.
I manage to do something as below, which reads each line and checks the number of character for each line, but the issue is that I am getting no value in %result% and echo(%result% prints a blank value. Any idea what am I doing wrong here? Thanks
#echo off
setlocal
for /f "tokens=* delims= " %%a in (REPORTS.csv) do (
set "line=%%a"
call :strlen result line
echo(%result%
if %result% EQU 7999 (
echo %%a
echo(short=%result%
) else (
echo %%a
echo(long=%result%
)
pause
)
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
Put this section into another subroutine, similar to :strlen
echo(%result%
if %result% EQU 7999 (
echo %%a
echo(short=%result%
) else (
echo %%a
echo(long=%result%
)
Note also that your main routine will continue into your subroutine when finished, so at end-of-file(reports.csv) the batch will execute :strlen one final time and exit through the EXIT
I'd recommend adding a
GOTO :EOF
Immediately before the :strlen label. This is understood by the processor to go to end-of-physiacl-file (the colon is required)
When a compound statement enclosed in parentheses is to be executed,
the statement is first parsed from the open parenthesis all of the
way to the matching close-parenthesis.
At this time, any %var% is replaced by that var's value from the
environment AT THE TIME IT IS PARSED (ie its PARSE-TIME value.)
THEN if the statement seems valid, it is executed.
There are three common ways of accessing the RUN-TIME value of the
variable (as a FOR loop executes, for instance.)
1/ SETLOCAL ENABLEDELAYEDEXPANSION which switches to a mode where
!var! may be used to access the runtime value of var
2/ CALL set var2=%%var%% to set the value of var2 from the
runtime value of var
3/ Executing a subroutine, internal or external within which %var%
will be the runtime value.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%i IN (1 2 3) DO (
ECHO START of run %%i
ECHO using ^!time^! : !time! - PARSE TIME was %time%
CALL ECHO using CALL %%%%TIME%%%% : %%TIME%%
CALL :report
timeout /t 5
ECHO using ^!time^! : !time!
CALL ECHO using CALL %%%%TIME%%%% : %%TIME%%
CALL :report
ECHO END of run %%i
ECHO.
)
GOTO :eof
:report
ECHO :report says TIME is %TIME%
GOTO :eof
A few items to note:
The instruction
IF ERRORLEVEL n echo errorlevel is n OR GREATER
ALWAYS interprets the RUN-TIME value of ERRORLEVEL
IF SET VAR ALWAYS interprets the RUN-TIME value of VAR
The magic variables like ERRORLEVEL and TIME should never
be SET. If you execute
SET ERRORLEVEL=dumb
then ERRORLEVEL will adopt the value dumb because the current
value in the environment takes priority over the system-assigned value.
You should use DelayedExpansion in if and for loops and take care of the brackets:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=* delims= " %%a in (REPORTS.csv) do (
set "line=%%a"
call :strlen result line
echo.!result!
if !result! EQU 7999 (
echo.%%a
echo.short=!result!
) else (
echo.%%a
echo.long=!result!
)
)
pause
goto:eof
:strlen <resultVar> <stringVar>
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
endlocal &set "%~1=%len%"
exit /b
Your code doesn't ever work in many areas.