I'm trying to extract every value for FileRef from a string I've already extracted out of a file. Unfortunately, the string is one line which makes it more difficult to use for /f "tokens=*".
The string is:
"<Cim:TrnTable_list><Cim:TrnTable Id="Root"><Cim:TrnElem Ref="3" FileRef="A1-FS.elt"/><Cim:TrnElem Ref="4" FileRef="A1-MS.elt"/><Cim:TrnElem Ref="9" FileRef="Product\Product-v1\Product-v1-MD.elt"/><Cim:TrnElem Ref="11" FileRef="Product\Product-v2\Product-v2-MD.elt"/><Cim:TrnElem Ref="12" FileRef="RunnerPart_Assembly#1.elt"/></Cim:TrnTable></Cim:TrnTable_list>"
How to get every value for FileRef inserted into a variable in the following format?:
A1-FS.elt?A1-MS.elt?Product\Product-v1\Product-v1-MD.elt?Product\Product-v2\Product-v2-MD.elt?RunnerPart_Assembly#1.elt
I mean, then I could loop trough them using for /f "delims=?" right?
Or is there a way to convert each ? in the above example to a 'new line' within one string, or maybe even better ideas to loop trough each FileRef-value?
Many thanks!
Squashman is right in his comment, use a language that is capable of handling XML data natively.
Anyway, if you insist on using pure Windows batch scripting, you could assemble a new string with ? symbols as separator like in the following script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~dpn0.txt" & rem // (path to file containing the line of text)
(set ^"_LF=^
%= empty line =%
^") & rem // (this constitutes a new-line character)
rem // Initialise collection variable:
set "COLL=?"
rem // Read line from file:
for /F "usebackq delims=" %%L in ("%_FILE%") do (
set "LINE=%%~L"
setlocal EnableDelayedExpansion
rem // Replace `><` by `>` + line-break + `<`:
set ^"LINE=!LINE:^>^<=^>^%_LF%%_LF%^<!^"
rem // Read one tag enclosed within `<` and `>`:
for /F "delims=" %%I in ("!LINE!") do (
endlocal
set "ITEM=%%I"
rem // Extract string between ` FileRef` and `/>`:
setlocal EnableDelayedExpansion
set "ITEM=!ITEM:* FileRef=!"
set "ITEM=!ITEM:/>=!"
rem // Check for `=`-sign after `FileRef`:
if "!ITEM:~,1!"=="=" (
rem // Remove leading `=` and surrounding `""`:
for /F "delims=| eol=|" %%F in ("!ITEM:~1!") do (
endlocal
set "NAME=%%~F"
rem // Assemble return string using `?` as separator:
setlocal EnableDelayedExpansion
for /F "delims=| eol=|" %%J in ("!COLL!!NAME!?") do (
endlocal
set "COLL=%%J"
setlocal EnableDelayedExpansion
)
)
)
)
endlocal
)
rem // Return collection variable:
setlocal EnableDelayedExpansion
echo(!COLL:~1,-1!
endlocal
endlocal
exit /B
Toggling delayed expansion is done in order not to have trouble with ! symbols.
Better than collecting all values in a single variable is to just loop through them in my opinion.
This is one other way to brute force this. This code will put each FileRef into its own variable and sequence the variable name up.
#echo off
FOR /F "delims=" %%G IN (line.txt) do set "line=%%G"
set i=0
:loop
set /a i+=1
set "line=%line:*FileRef=%"
FOR /F "tokens=1* delims==/" %%G IN ("%line%") DO (
set "var%i%=%%~G"
set "line=%%H"
)
echo "%line%"|find /I "fileref" >nul 2>&1 &&GOTO loop
set var
pause
When executed it will output this.
C:\BatchFiles\SO\XML>bruteforce.bat
var1=A1-FS.elt
var2=A1-MS.elt
var3=Product\Product-v1\Product-v1-MD.elt
var4=Product\Product-v2\Product-v2-MD.elt
var5=RunnerPart_Assembly#1.elt
Press any key to continue . . .
If you don't want the data assigned into their own individual variables you can just use the %%G meta-variable directly inside the FOR command.
Related
I am capturing a m3U file on a daily basis but wish to parse part of it to another file with the few channels I need.
For example I have renamed my m3U to Test.txt file which say has the following fictional structure:
#EXTINF:0,ABC
#live link 1
#EXTINF:0,XYZ
#live link 2
#EXTINF:0,UVW
#live link 3
I would just like to capture say the line staring from "#EXTINF:0,XYZ" and say the line beneath it to end up with a Output.txt as follows:
#EXTINF:0,XYZ
#live link 2
I know that one needs to use the For loop but I am a bit of a noob on this area.
Put this code into the file filter.cmd.
#echo off
set INPUT=%1&set MATCH=%2& set MATCHED=0
for /f "delims=" %%a in (%INPUT%) do call :line "%%~a"
goto :eof
:line
set EXT=&TITLE=&
for /f "tokens=1 delims=:" %%a in ("%~1") do set EXT=%%~a
for /f "tokens=1,2,* delims=:," %%a in ("%~1") do set TITLE=%%~c
if "%EXT%" == "#EXTM3U" echo %~1
if "%EXT%" == "#EXTINF" (
set MATCHED=0
echo %TITLE%| findstr /l %MATCH% >nul && set MATCHED=1
)
if %MATCHED%==1 echo %~1
Use example:
filter.cmd input_file.m3u XYZ > output_file.m3u
Here is some explanation:
Every input line is split using for /f with tokens and delims.
MATCHED is set if the line begins with #EXTINF and the rest contains the string to match (second argument).
if MATCHED is set, the lines are output until next #EXTINF.
I would do it like this, supposing the .m3u file does not contain trailing white-spaces in the lines preceded by #EXTINF, like your sample data does:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "FILE=%~1"
set "HEADER=#EXTM3U"
set "PREFIX=#EXTINF"
set "MATCH=%~2"
set "FLAG="
for /F usebackq^ delims^=^ eol^= %%L in ("%FILE%") do (
if defined FLAG (
echo(%%L
set "FLAG="
)
for /F "delims=:" %%P in ("%%L") do (
if "%%P"=="%HEADER%" (
echo(%%L
) else if "%%P"=="%PREFIX%" (
set "LINE=%%L"
setlocal EnableDelayedExpansion
if /I "!LINE:*,=!"=="!MATCH!" (
echo(!LINE!
endlocal
set "FLAG=#"
) else endlocal
)
)
)
endlocal
exit /B
Call the script like this, supposing it is saved as extract-entry.bat:
extract-entry.bat "input_file.m3u" "XYZ" > "output_file.m3u"
The script walks through the given .m3u file line by line. It returns the current line unedited and resets variable FLAG, if variable FLAG is set, which is not the case at the beginning.
Then it looks for #EXTINF. If found (e. g., #EXTINF:0,XYZ), the string after the comma (XYZ) is compared against the given search string. If matched, the current line is output and FLAG variable is set now in order to get the following line too.
The header line #EXTM3U is always output.
Toggling delayed expansion makes this script robust against all characters that have special meaning to the command interpreter without losing them.
I was searching for a batch script which edits a specific and known line in another batch-file.
I found this solution (Stackoverflow: Batch - edit specified line in text file) and it was almost workiing properly. The only problem I had with the script from Endoro is that it deletes the colons at the start of a line which I don't want to edit. Is there a way to avoid this from happening?
Help would be much appreciated! Thanks in advance!
#ECHO OFF &SETLOCAL
:Input
set /p version=Please Enter Version:
:Replacement
SET "file=test.bat"
SET /a Line#ToSearch=4
SET "Replacement=set jversion = %Version%_x86"
(FOR /f "tokens=1*delims=: " %%a IN ('findstr /n "^" "%file%"') DO (
SET "Line=%%b"
IF %%a equ %Line#ToSearch% SET "Line=%Replacement%"
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO(!Line!
ENDLOCAL
))>"%file%.new"
TYPE "%file%.new"
MOVE "%file%.new" "%file%"
for /F treats subsequent delimiters as one. findstr /N precedes every line with a line number and a colon. So for instance if the third line is :abcd ef, for /F receives 3::abcd ef. After parsing the two tokens, you will get 3 and abcd ef. That is why leading colons disappear.
To overcome this, use sub-string replacement syntax; supposing variable Line contains the entire line including the line number prefix (string 3::abcd ef from above), use SET "Line=!Line:*:=!" to remove everything up to and including the first colon (so the resulting string is :abcd ef).
To get the line number, use another for /F loop with : as delimiter, fetching the first token only.
Here is the fixed script:
#ECHO OFF &SETLOCAL
:Input
set /p version=Please Enter Version:
:Replacement
SET "file=test.bat"
SET /a Line#ToSearch=4
SET "Replacement=set jversion = %Version%_x86"
(FOR /f "delims=" %%a IN ('findstr /n "^" "%file%"') DO (
SET "Line=%%a"
rem // Use a `for /F` loop to extract the line number:
for /F "delims=:" %%N in ("%%a") do set "LNum=%%N"
SETLOCAL ENABLEDELAYEDEXPANSION
rem // Use sub-string replacement to split off
rem // the preceding line number and one colon:
SET "Line=!Line:*:=!"
IF !LNum! equ %Line#ToSearch% SET "Line=%Replacement%"
ECHO(!Line!
ENDLOCAL
))>"%file%.new"
TYPE "%file%.new"
MOVE "%file%.new" "%file%"
I want to be able to replace a line in a properties file but i only know part of the line string at any one time
Heres the line i want to replace: mb.datasource.password=ENC(8dF45fdD)
with this: mb.datasource.password=apassword
What i have just now is this
#echo off &setlocal
set "search=mb.datasource.password="
set "replace=mb.datasource.password=apassword"
set "textfile=mb.properties"
set "newfile=mb-new.properties"
(for /f "delims=" %%i in (%textfile%) do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
echo(!line!
endlocal
))>"%newfile%"
This ends up giving me mb.datasource.password=apassword=ENC(8fFdeUdK)
I can't just find the full string it needs to only be mb.datasource.password= because the part after the equals changes
Any help would be greatly appreciated?
You can do it with batch. I put together a quick script and it worked for me:
#ECHO OFF
SETLOCAL EnableExtensions
SET SourceFile="mb.properties"
SET OutputFile="mb-new.properties"
SET "FindKey=mb.datasource.password"
SET "NewValue=apassword"
REM Basic parse for INI file format.
(FOR /F "usebackq eol= tokens=1,* delims==" %%A IN (`TYPE %SourceFile%`) DO (
REM If the key value matches, build the line with the new value.
REM Otherwise write out the existing value.
IF /I "%%A"=="%FindKey%" (
ECHO %%A=%NewValue%
) ELSE (
ECHO %%A=%%B
)
)) > %OutputFile%
REM Replace old with new.
DEL %SourceFile%
MOVE %OutputFile% %SourceFile%
ENDLOCAL
A few notes about the above:
I assumed basic INI file format.
The FOR loop will skip blank lines so they would be removed from the new output file. There are ways around this using tricks with the FIND command if the blanks are needed.
If you have special chars (% or ! especially) - this may cause some problems, but if you have just "normal" text then it should be fine.
I am trying to create a batch file that will remove some special characters from a .txt file and append it to another file but the variable is unable to hold the full 1 million++ words in the text file.
Is there any way to make the variable hold all the words or at least split it into groups/sets?
The text look like this
{"One:1","two:2","three:4","four:3","five:5","EG:[512]","sets:{559,212,333,940}"};{"One:9","two:3","three:2","four:1","five:6","EG:[513]","sets:{551,215,331,944}"};...
So far I have tried using a FOR loop to append it to many different files then go through each files later using goto but it just doesn't work like it should.
edited - Previous answer at the bottom. As jeb comments this is faster, not sure if it will even be usable but faster (but, of course, being faster than the previous code is an easy task)
#echo off
setlocal enableextensions disabledelayedexpansion
<"in.txt" >"out.txt" call :process
goto :Eof
:process
set /p "buffer=" || goto :eof
set "buffer=%buffer:"=%"
set "buffer=%buffer:,= %"
set "buffer=%buffer:{=%"
set "buffer=%buffer:}=%"
set "buffer=%buffer:[=%"
set "buffer=%buffer:]=%"
set "buffer=%buffer:;=%"
set "buffer=%buffer::=%"
set "buffer=%buffer:0=%"
set "buffer=%buffer:1=%"
set "buffer=%buffer:2=%"
set "buffer=%buffer:3=%"
set "buffer=%buffer:4=%"
set "buffer=%buffer:5=%"
set "buffer=%buffer:6=%"
set "buffer=%buffer:7=%"
set "buffer=%buffer:8=%"
set "buffer=%buffer:9=%"
<nul set /p "=%buffer%"
goto :process
WARNING : This should not have ever been written. IT IS PAINFULLY SLOW.
Once it has been said,
#echo off
setlocal enableextensions disabledelayedexpansion
rem File configuration
set "inputFile=data.txt"
set "outputFile=out.txt"
rem Variable to hold a cariage return used to show that the
rem script is still working
set "CR="
for /f %%c in ('copy /Z "%~f0" nul') do if not defined "CR" set "CR=%%c"
rem A temporary file will be used.
for %%t in ("%temp%\%~nx0.%random%%random%.tmp") do (
echo Splitting input file into temporary file
> "%%~ft" (
( %= Split the input file into one character per line =%
cmd /u /q /c"type ""%inputFile%""" | cmd /a /q /c"find /v "" "
%= Ensure we have a terminator to empty buffer (keep reading...) =%
<nul set /p"=;"
)|( %= Remove non needed characters =%
findstr /i /r /c:"^[a-z,;]"
)
)
echo Splitting done
echo Starting to read temporary file
echo %time% Here we go ...
set "buffer=#"
rem All data to stdout will be placed into the output file
> "%outputFile%" (
for /f usebackq^ delims^=^ eol^= %%a in ("%%~ft") do (
rem Concatenate one character to the buffer
setlocal enabledelayedexpansion
for /f delims^=^ eol^= %%b in ("!buffer!") do (
endlocal
set "buffer=%%b%%a"
)
rem We will use the semicolon as a delimiter to do
rem partial processing of the input data
if "%%a"==";" (
rem Execute the required processing on the buffer
call :processSection buffer
setlocal enabledelayedexpansion
< nul (
rem Write the processed buffer to stdout
set /p "=!buffer!"
rem Show we are still working
>con set /p "=!time! ... still working ... !CR!"
)
endlocal
set "buffer=#"
)
)
)
rem Processed temporary file can be removed
) & del "%%~ft"
echo(
echo %time% ... Done
goto :eof
:processSection varName
setlocal enableextensions disabledelayedexpansion
rem Retrieve the data from the indicated variable
setlocal enabledelayedexpansion
for /f delims^=^ eol^= %%a in ("!%~1!") do (
endlocal
set "line=%%a"
)
rem The passed buffer includes an initial filler character
set "line=%line:~1%"
rem Process buffer
set "line=%line:,= %"
set "line=%line:;= %"
set "line=%line: = %"
rem Return buffer to the caller
endlocal & set "%~1=%line%"
goto :eof
It was written just for testing but, please, use any other thing.
#echo off
setlocal enableextensions disabledelayedexpansion
for /r %f in (xis_a*) do More +1 %~nxf >> No_header_%~nxf
set "search=:20:"
set "replace={:20:"
for /f "delims=" %%i in ('type (No_header_*.txt) ^& break ^> (No_header_*.txt) ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>No_header_*.txt echo(!line!
endlocal
)
am trying to skip the header line in a text file and replace :20: with {:20:. i have written and have achieved almost.. please try to help me am totally new to this
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
set "search=:20:"
set "replace={:20:"
for /r "%sourcedir%" %%f in (xis_a*) do (
REM DEL "%destdir%\No_header_%%~nf.txt" >NUL 2>nul
for /f "usebackqskip=1delims=" %%i in ("%%f") do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%destdir%\No_header_%%~nf.txt" echo(!line!
ENDLOCAL
)
)
GOTO :EOF
This should solve the problem.
In your code, the initial setlocal is establishing the default condition, so I omitted it.
You have %f in your 'for /r - the % before the metavariable f needs to be doubled.
The more ceremony isn't required - for /f has a skip option which will allow the first n lines to be skipped.
So - all that is required is to get the list of files generated by the for...%%f... loop and with each, process each individual file, skipping the first line. Quoting the filename appearing in %%f is simply a safety-measure to allow for filenames containing separators, but this means that usebackq needs to be invoked to tell for /f that the list given is not a literal string (which it will assume for a "quoted string,") but a filename.
Then, since %%f is in-context for the %%i loop, you can select the target filename by using %%~nf. I'm not sure whether you want ~nf or ~nxf (adding a second .txt extension) so I elected to use ~n alone. Note that your use of * is doomed - that means "all files matching" - probably not quite what you want, and cmd will get very confused.
I've aded a remmed-out del command to allow the destination file to be deleted - just remove the rem if required, otherwise the data will be appended to any exiating file.
I use my u: drive for testing, and have left my source and destination names in place. No doubt you would need to change those to suit your system.