How to parse command outptut and store in a file - batch-file

I am executing command and parsing the command based on space, Formatting it and storing in a file.
This is how I have written the batch script
for /f "tokens=2,4,5" %G IN ('command') DO echo Id:%%G:~0,-1% Timestamp:%H %I > C:\Users\TJ\Documents\out1.txt
I am getting the output like this
Id:3495068:~0,-1; Timestamp:2023/01/25 14:57:18
But I am trying to trim the ";" semicolon but it's not trimmimg instead it's adding the trim logic to output.
I am expecting output like.
Id:3495068 Timestamp:2023/01/25 14:57:18
Is anything I am missing here.

Consecutive delimiters are treated as one. You split by Spaces, if you just add the semicolon to the delimiters, every space, every semicolon and every combination of those will be treated as (one!) delimiter.
for /f "tokens=2,4,5 delims=; " %%G IN ('echo transaction 3495068; promote; 2023/01/25 14:57:16 ; user: thejas') DO echo Accurev Build ID:%%G Timestamp:%%H
Output:
Accurev Build ID:3495068 Timestamp:2023/01/25
Note: the space must be the last char in delims=, or you get a syntax error.

You cannot substring a metavariable like %%G. You can only substring ordinary variables.
for /f "tokens=2,4,5" %G IN ('command') DO set "buildid=%G"&call echo Accurev Build Id:%buildid:~0,-1% Timestamp:%H %I> C:\Users\TJainMJ\Documents\out1.txt&set "buildid="
should solve your problem, but it would be much better as a batch file where you don't have to retype the line every time you want to use it.
#ECHO OFF
SETLOCAL
for /f "tokens=2,4,5" %%G IN ('command') DO set "buildid=%%G"&>"u:\q75219686.txt" call echo Accurev Build Id:%%buildid:~0,-1%% Timestamp:%%H %%I&set "buildid="
TYPE "u:\q75219686.txt"
GOTO :EOF
where "u:\q75219686.txt" is my destination file used for testing

Related

How to extract values from an XML file with no newline characters (very long single line)?

I am trying to extract values for test_count, test_fail_count, test_pass_count from an XML file. This XML file has just one very long line:
<?xml version="1.0" encoding="UTF-8"?><ROOT test_count="22" test_fail_count="1" test_pass_count="21".......</ROOT>
Magoo helped me with the script, see his answer on my previous question
How to match strings from an XML file using batch and assign to variable?
This script worked initially. But when I incorporated this into my larger overall script, it failed. And I have not been able getting this script working again as expected since making this modification.
Any thoughts on how to debug this?
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\Report.xml"
SET "testcount="
SET "testfailcount="
echo forloop
FOR /f "usebackqdelims= " %%a IN ("%filename1%") DO (
SET "xmlline=%%a"
CALL :process
)
ECHO test count=%testcount% test fail count=%testfailcount%
GOTO :EOF
:process
echo in process
:: dispose of awkward characters
SET "xmlline=%xmlline:?= %"
SET "xmlline=%xmlline:>= %"
SET "xmlline=%xmlline:<= %"
CALL :select %xmlline%
GOTO :EOF
:select
echo in select
IF /i "%~1"=="" GOTO :EOF
IF DEFINED testcount IF DEFINED testfailcount GOTO :EOF
IF /i "%~1"=="test_count" SET /a testcount=%~2
IF /i "%~1"=="test_fail_count" SET /a testfailcount=%~2
SHIFT
GOTO select
GOTO :EOF
try the xpath.bat - it can extract values from xml files by an xpath expression and does not require installation of external tools:
call xpath.bat "report.xml" "//ROOT/#test_count"
call xpath.bat "report.xml" "//ROOT/#test_fail_count"
As in the metadata is pointed that file should be utf-8 you can check the encodings of the files on both machines.
The reason for not anymore working code is in the command line
FOR /f "usebackqdelims= " %%a IN ("%filename1%") DO (
There is a space character after the equal sign which results in splitting the line read from XML file up into multiple tokens using the space character as delimiter. So instead of getting entire XML file contents assigned to loop variable a, just the string up to first space character is assigned to the loop variable. For that reason the environment variable xmlline gets assigned just <?xml instead of the entire line read from XML file.
Change the line to
FOR /f "usebackq delims=" %%a IN ("%filename1%") DO (
There is no space after equal sign, but one between usebackq and delims=.
Or use the command line below as Magoo posted in his answer with no space after equal sign, but also no space between the two options usebackq and delims=.
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
Magoo explained in his comment also why the space character between the two options usebackq and delims= is not really necessary, but which I suggest to add for easier reading the options.
usebackq results in interpreting the file name enclosed in double quotes as file name and not as string to split up into tokens.
delims= with no characters specified after equal sign disables default splitting up of line read from file on spaces and horizontal tabs.
Open a command prompt window, run for /? and read the output help pages for help on for /F and its options.

How can i get all of the 2nd line only of my stats.txt file?

i currently have this command for a batch file
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i
with the contents of stats.txt being
Title = Subaru's Great Rehab Strategy
URL = http://dynasty-scans.com/chapters/subarus_great_rehab_strategy
Tags = Subaru x Tsukasa[|]Yuri[|]
No. of Pages = 3
^ NOTE: the final line is actually blank
the idea of the line of code is to return the 2nd line with URL. the end goal would be that i would run this line in some sort of loop going though a series of ~12000+ stats.txt files and collecting all the URL lines into a single file
but when i run the command i get this
as you can see it has skipped the first line but it's cutting off where the n in dynasty and outputting the last 3 lines.
now if i remove delims=\n i get the same 3 lines but i don't get the first word before the space which seems to indicate that the value of delims is what splits a line into "tokens" which then i just grab the first one (and space must be the default)
when i go into notepad++, open the Find and Replace Dialog, turn Search Mode to extended and look for "\r\n" i get taken to the end of each line which is why i chose delims to be \n assuming this would then make the entire line one token
So my question is How can i get all of the 2nd line only of my stats.txt file?
The for /f loop already treats the carriage return and / or line feed as an end-of-line. No need to specify it as a delimiter. With delims=\n you're actually saying that all literal backslashes and letter n's should be treated as token delimiters. If you want the whole line, what you want is "skip=1 delims=".
Just out of habit, when reading the contents of a file with a for /f loop, I find it useful to enable usebackq just in case the filename / path contains a space or ampersand. That allows you to quote the filename to protect against such potential treachery.
#echo off
setlocal
for /F "usebackq skip=1 delims=" %%I in ("stats.txt") do if not defined URL set "URL=%%~I"
echo %URL%
Put into context, to use this to read many files named stats.txt and output the URLs into a single collection, enclose the whole thing in another for loop and enable delayed expansion.
#echo off
setlocal
>URLs.txt (
for /R %%N in ("*stats.txt") do (
for /F "usebackq skip=1 delims=" %%I in ("%%~fN") do (
if not defined URL set "URL=%%~I"
)
setlocal enabledelayedexpansion
echo(!URL!
endlocal
set "URL="
)
)
echo Done. The results are in URLs.txt.
If you want to strip the "URL = " from the beginning of each line and keep only the address, you could try changing your for /F parameters to "usebackq skip=1 tokens=3" if all the files follow the same format of URLSpace=Spacehttp://etc.. If you can't depend on that, or if any of the URLs might contain unencoded spaces, you could also change echo(!URL! to echo(!URL:*http=http!
You don't need to use a FOR /F loop, you can also read it with a SET /P
setlocal EnableDelayedExpansion
< stats.txt (
set /p line1=
set /p URL_Line=
)
echo(!URL_Line!
Try this from the command line:
(for /F "tokens=1* delims=:" %i in ('findstr "URL" stats*.txt') do echo %j) > output.txt
the idea ... is to return the 2nd line with URL
If you want to insert this line in a Batch file, just double the percent signs.
Try this from the prompt:
(for /f "tokens=1*delims=]" %a in ('find /v /n "" *.csv^|findstr /l /b "[2]"') do #echo %b)>u:\r1.txt
Where - I used *.csv for testing (substitute your own filemask) and I used u:\r1.txt for the result - substitute as seems fit (but don't output to a file tat fits your selected filemask !)
It works by prefixing each line in each file with a bracketed number [n] (find - /n=and number /v lines that do not match "" - an empty string); then selecting those lines that /l - literally /b at the beginning of the line match "[2]".
The result is all of the second-lines of the files, preceded by the literal "[2]". All we need to do then is tokenise the result, first token up to delimiter "]" will be "[2" assgned to %%a and remainder-of line (token *) will be assigned to %%b
Have you tried
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i && goto :eof
I haven't tested it as I don't have access to a Windows machine at the moment, but that should exit the for-loop after the first iteration, which is what you want.

bat file: Use of if, for and a variable all together

Here goes:
I want to add text to a file if it exists. The text is to go on each line of the file at the very end. The text to be added is "Note: certain conditions apply"
I found syntax to check if the file exists, and syntax for a for loop to add text at the end of the line, but they do not seem to work at the same time in the bat file.
Also, advice varies about variable names, using "%" and using the quote marks themselves,
so here is what I have (assume everything takes place in the same directory)....
#echo off
setLocal EnableDelayedExpansion
PAUSE
REM File to be modified -- 1
SET FileToModify1=abcd.txt
SET SaveFile1=abcd1.txt
PAUSE
IF EXIST "%FileToModify1%" (
echo Yes
)
IF EXIST "%FileToModify1%" (
for /f "tokens=* delims= " %%a in "%FileToModify1%" do (
echo %%a Note: certain conditions apply >> "%SaveFile1%"
)
)
PAUSE
Does anyone have a suggestion on what to do here?
Also, is it better to have quotes around abcd.txt or not?
Why all the mysterious "%" around variables?
Please also see loop through file saving to variable for a much more efficient solution to the same problem....
You need this (works):
for /f "tokens=* delims= " %%a in (%FileToModify1%) do (
Where you have (doesn't work, wrong syntax):
for /f "tokens=* delims= " %%a in "%FileToModify1%" do (
From the online help (via for /?)
For file names that contain spaces, you need to quote the filenames with
double quotes. In order to use double quotes in this manner, you also
need to use the usebackq option, otherwise the double quotes will be
interpreted as defining a literal string to parse.
Also, is it better to have quotes around abcd.txt or not?
It's required if your file name has spaces, otherwise it's optional.Your file name doesn't have spaces so I used the simpler approach, no quotes.
Why all the mysterious "%" around variables?
Well it doesn't have to be "mysterious". Here's an example, from the command line:
> set fname=MyFile.txt
> echo %fname%
MyFile.txt
The %'s around the variable name are seen by the shell and trigger the variable expansion. To see the online help, type set /?, and you'll get more details than you can stand.

How to pass additional extra arguments to a batch file?

I would like to use first 2 arguments passed to a batch file and then append everything else to the command executed in the batch file.
Is there an easy way to do that?
// batch file
command -Duser=%1 -Dpwd=%2 {? how to pass all remaining arguments on the command line}
The batch will be executed like this
batch.bat USR PWD -Dargument1=1 -Dargument2=2
Effectively, I would like the following to be executed
command -Duser=USR -Dpwd=PWD -Dargument1=1 -Dargument2=2
The for command should give you what you want:
for /f "tokens=1,2*" %%a in ("%*") do echo command -Duser=%%a -Dpwd=%%b %%c
From the help for for (for /?):
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do #echo %i %j %k
would parse each line in myfile.txt, ignoring lines that begin with
a semicolon, passing the 2nd and 3rd token from each line to the for
body, with tokens delimited by commas and/or spaces. Notice the for
body statements reference %i to get the 2nd token, %j to get the
3rd token, and %k to get all remaining tokens after the 3rd. For
file names that contain spaces, you need to quote the filenames with
double quotes. In order to use double quotes in this manner, you also
need to use the usebackq option, otherwise the double quotes will be
interpreted as defining a literal string to parse.
So in my solution, %%a has the value of the first argument passed to the script, %%b the value of the second argument, and %%c the remaining arguments.
When I run batch.bat USR PWD -Dargument1=1 -Dargument2=2 from a command prompt I get:
command -Duser=USR -Dpwd=PWD -Dargument1=1 -Dargument2=2
Remove the echo after the do to call your command with the arguments.
Patrick Cuff's solution usually works, but it will fail if either of the first 2 arguments contain quoted spaces or special characters. It will also fail if the arguments are delimited with something other than spaces. The FOR /F delimiters could be expanded to include , ; and =, but then it will fail if the argument contains any of those quoted delimiters.
The following improvement supports quoted special characters like & and |, but the argument delimiters are still a problem.
#echo off
setlocal enableDelayedExpansion
set args=%*
for /f "tokens=1,2*" %%a in ("!args!") do (
endlocal
echo arg1=%%a arg2=%%b remainder=%%c
)
EDIT Here is a simpler modification using USEBACKQ that achieves the same thing
for /f "usebackq tokens=1,2*" %%a in ('%*') do echo arg1=%%a arg2=%%b remainder=%%c
End edit
Unfortunately a truly general solution requires a GOTO loop that parses every parameter from 3 on. The solution below should work as long as all quotes are balanced and all special characters are quoted.
#echo off
setlocal
set "args="
:parseArgs
set args=%args% %3
shift /3
if "%~3" neq "" goto :parseArgs
if (%3) equ ("") goto :parseArgs
echo arg1=%1 arg2=%2 remainder=%args%
To eliminate all limitations would require drastic measures - see How to receive even the strangest command line parameters?
This might be what you want
C:>help shift
Changes the position of replaceable parameters in a batch file.
SHIFT [/n]
If Command Extensions are enabled the SHIFT command supports
the /n switch which tells the command to start shifting at the
nth argument, where n may be between zero and eight. For example:
SHIFT /2
would shift %3 to %2, %4 to %3, etc. and leave %0 and %1 unaffected.

How to extract all instances of a specific XML tag attribute using Windows batch

I have an XML file and I need to extract
testname
from all the instances of
<con:testSuite name="testname"
within the XML file.
I am not quite sure how to approach this, or whether this is possible in batch.
Here is what I have thought so far:
1) Use FINDSTR and store every line that has
<con:testSuite name=
in a variable or a temporary file, like this:
FINDSTR /C:"<con:testSuite name=" file.xml > tests.txt
2) Somehow use that file or variable to extract the strings
Note that there might be more than one instance of the matching string in the same line.
I am a novice at batch and any help is appreciated.
Parsing XML is very painful with batch. Batch is not a good text processor to begin with. However, with some amount of effort you can usually extract the data you want from a given XML file. But the input file could easily be rearranged into an equivalent valid XML form that will break your parser.
With that disclaimer out of the way...
Here is a native batch solution
#echo off
setlocal disableDelayedExpansion
set input="test.xml"
set output="names.txt"
if exist %output% del %output%
for /f "delims=" %%A in ('findstr /n /c:"<con:testSuite name=" %input%') do (
set "ln=%%A"
setlocal enableDelayedExpansion
call :parseLine
endlocal
)
type %output%
exit /b
:parseLine
set "ln2=!ln:*<con:testSuite name=!"
if "!ln2!"=="!ln!" exit /b
for /f tokens^=2^ delims^=^" %%B in ("!ln2!") do (
setlocal disableDelayedExpansion
>>%output% echo(%%B
endlocal
)
set "ln=!ln2!"
goto :parseLine
The FINDSTR /N option is only there to guarantee that no line begins with a ; so that we don't have to worry about the pesky default FOR "EOL" option.
The toggling of delayed expansion on and off is to protect any ! characters that may be in the input file. If you know that ! never appears in the input, then you can simply setlocal enableDelayedExpansion at the top and remove all other setlocal and endlocal commands.
The last FOR /F uses special escape sequences to enable the specification of a double quote as a DELIM character.
Answer to additional question in comment
You cannot simply put the additional constraint in the existing FINDSTR command because it will return the entire line that has a match. Remember you said yourself, "there might be more than one instance of the matching string in the same line". The first name might start with the correct prefix, and the 2nd name on the same line might not. You only want to keep the one that starts appropriately.
One solution is to simply change the echo(%%B >>%output% line as follows:
echo(%%B|findstr "^lp_" >>%output%
The FINDSTR is using a regular expression meta-character ^ to specify that the string must start with lp_. The quotes have already been removed at this point, so we don't have to worry about them.
However, you may run into a situation in the future where you must include " in your search string. Plus it might be marginally faster to include the lp_ screen in the initial FINDSTR so that :parseLine is not called unnecessarily.
FINDSTR requires that search string double quotes are escaped with a back slash. But the Windows CMD processor also has its own rules for escaping. Special characters like > need to be either quoted or escaped. The original code used quotes, but you want to include a quote in the string, and that creates unbalanced quotes in your command. Windows batch generally likes quotes in pairs. At least one of the quotes must be escaped for CMD as ^". If the quote needs to be escaped for both CMD and FINDSTR, then it looks like \^".
But any special characters within the string that are no longer functionally quoted from a CMD perspective must be escaped using ^ as well.
Here is one solution that escapes all special characters. It looks awful and is very confusing.
for /f "delims=" %%A in ('findstr /n /c:^"^<con:testSuite^ name^=\^"lp_^" %input%') do (
Here is another solution that looks much better, but it is still confusing to keep track of what is escaped for CMD and what is escaped for FINDSTR.
for /f "delims=" %%A in ('findstr /n /c:"<con:testSuite name=\"lp_^" %input%') do (
One way to keep things a bit simpler is to convert the search into a regular expression. A single double quote can be searched using [\"\"]. It is a character class expression that matches either a quote or a quote - silly I know. But it keeps quotes paired so that CMD is happy. Now you don't have to worry about escaping any characters for CMD, and you can concentrate on the regex search string.
for /f "delims=" %%A in ('findstr /nr /c:"<con:testSuite name=[\"\"]lp_" %input%') do (

Resources