BATCH Iterate FOR /F variables - batch-file

I have simple files that look like:
Submit.log
Submitting job(s)..
2 job(s) submitted to cluster 528.
Submitting job(s)..
2 job(s) submitted to cluster 529.
I need then to output only the last number from the second line (the cluster), in this case the desired output would be:
528
529
I tried using the script below but I dont know why it is not working. I need to do it using batch, it is a restriction of our system.
#ECHO OFF
FOR /F "tokens=6" %%g IN (Submit.log) DO (
set Job=%%g
echo %Job:.=%
)
An elegant solution would definitively be a plus, but if someone could help me out with a simple solution I would appreciate.

Here is a one liner that works. The FINDSTR search can easily be adapted to make the search very precise.
#echo off
for /f "tokens=6 delims=. " %%N in ('findstr /c:"submitted to cluster" "submit.log"') do echo %%N
The paxdiablo solution can be simplified as well so that it does not need to modify the value therefor it doesn't need delayed expansion. There is no need to capture all the tokens, just the ones you want to use. The delimiter is set to look for dot and space (the default was space and tab). As paxdiablo said, more tokens can be tested to make the search more precise, but this is not as convenient as FINDSTR.
#echo off
for /f "tokens=5,6 delims=. " %%A in (submit.log) do if %%A==cluster echo %%B

The following script will do the job:
#setlocal enableextensions enabledelayedexpansion
#echo off
for /f "tokens=1-6" %%a IN (submit.log) do (
if %%c==submitted (
set job=%%f
echo !job:.=!
)
)
endlocal
And, since I'm basically the modest type, I'll stop short of contending that it's both elegant and simple :-)
It basically only looks at those lines where the third word is submitted (you can also use additional rules if this proves problematic) and then modifies and outputs the sixth word.
Note the use of delayed expansion (using ! instead of %) That's needed to ensure the variables aren't evaluated before the loop even begins.

Related

Batch loopvariable manipulation

I have a small problem with a .bat file that I have to build to manipulate a specific .csv.
I want the .bat to read the line of the file, and then check for the first three letters of that line. At the end there should be n-files where file xxx.csv contains the lines of the input.csv with xxx as the first three letters of line.
First things first, I don't even know if it is possible to do it this job in a batch-file, because the file has >85000 lines and may even get much bigger. So if it is impossible you can directly tell me that.
for /f "delims=" %%a in (input.CSV) DO (
echo %%~a:~0,3
pause
)
I want to "output" the first three letters of %%a.
It would be great if you could help me.
Phil
Substring substitution only works with environment variables (%var%), but not with metavariables (%%a) (as Mofi already commented). And because you are setting and using a variable within the same command block, you need delayed expansion:
setlocal enabledelayedexpansion
for /f "delims=" %%a in (input.CSV) DO (
set "var=%%~a"
echo !var:~0,3!
pause
)
(there are methods without delayed expansion, but they make use of call, which slows things down)

Using Windows ren command to remove ! from filename

I have a bunch of files that are in the following format
filename-!#.ext
where # is an incrementing number to prevent collisions. I would like to just remove the ! from the filename so it reads as
filename-#.ext
It seems like I could do this with the ren command and wildcards but having a hard time getting it to work. I tried running this:
ren *!?.ext *?.ext
My thought is the * should match the filename- part, then !, then the ? for the numeric. However, the resulting file is named this:
filename-!#.ext.ext
and I can't quite figure out why.
The filename part can be dynamic, but won't contain any !'s. Any ideas?
I think you'll have to resort to a small batch file for this:
#echo off
setlocal
for %%a in (*!*.ext) do call :remove "%%~a"
goto :eof
:remove
set "FROM=%~1"
set "TO=%FROM:!=%"
ren "%FROM%" "%TO%"
goto :eof
The above will – for all files containing an exclamation mark and extension ext – call the remove function. This takes the original name (FROM) and determines the new name (TO) by using the %var:find=replace% syntax to remove any exclamation mark (replaces it with an empty string).
Notes
You cannot use find/replace with either %%a or %0 type variables, so you have to assign it to a named variable first.
I originally tried doing this "inline" with the for statement (e.g. for ... ( ending )) but to do that, you would have to enable delayed-expansion (because you would need to access a named variable in a loop). However, delayed-expansion uses ! (instead of %) to reference variables and this got in the way of the ! we were trying to remove. There may be a way of doing this, but I haven't found it. Using call will be slightly slower, but not significantly unless you've got thousands of files.
You don't really need to create TO (you could perform the replacement on the ren command-line) but I used it for clarity.
This will work for all files with an exclamation mark: it doesn't check the bit after is numeric.
I think it is not a good idea to let rename select the files, but instead do it with a for-loop and then execute the rename for every file:
for %%F in (*!*) do (
set "nxF=%%~nxF"
call ren "%%nxF%%" "%%nxF:!=%%"
)
You really have to use call here instead of delayedExpansion, because delayedExpansion would destroy the rename-arguments (because they contain !)
Based upon your provided information and just for the sake of offering something a little different, you could let delayed expansion do the work for you, (as it will remove the unwanted exclamation mark for you).!
#For %%A In ("*-!?.ext") Do #Set "_=%%A" & SetLocal EnableDelayedExpansion & Ren "!_!" "%%A" & EndLocal
You could probably also do it with a nested for loop:
#For %%A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %%B In ("%%A") Do #Ren "%%A" "%%B%%C"
…and from the Command Prompt:
For %A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %B In ("%A") Do #Ren "%A" "%B%C"

Batch FOR loop processing drops equal and comma characters

Any batch experts able to explain the following behavior?
I am trying to have a variable which contains the permissions required to be used by a net share command. The variable's name is %GRANT%
I have found that I cannot simply enter "net share ShareName=D:\Path %GRANT%"
However, it does appear that the following will work possibly:
"FOR /F "delims=" %%A in ('echo net share ShareName="D:\Path" %GRANT%') do (%%A)
Here is the issue. The FOR loop described above is NOT working. But it is not working because "it just wont' work." It is not working, because the FOR loop is dropping the comma and equal characters out of the command.
Please see the attached screenshot. You can see that %GRANT% does indeed contain a comma (1.). You can also see that the "IN" command contains an equal sign as should be present in net share. however, when I echo the resulting command in order to troubleshoot what's going on, I see that the FOR loop processing is dropping both the equal and comma characters out of the result (2.).
Can anyone explain this and is there something I can do to make it leave those characters in?
#ECHO OFF
SETLOCAL
SET "grant=/grant:Everyone^,FULL^=testing"
FOR /F "delims=" %%A in ('echo net share ShareName="D:\Path" %GRANT%') do (ECHO %%A)
SET "grant=/grant:Everyone,FULL=testing"
FOR /F "delims=" %%A in ("net share ShareName="D:\Path" %GRANT%") do (ECHO %%A)
GOTO :EOF
Here's two slightly different ways to do what you appear to want to do.
The problem is that commas, semicolons, tabs and often = are seen as separators. Usng the caret or "quoting the string" overcomes the problem.

Batch script to grab lines with findstr without filepath

I've got a log file that monitors a large system including requests and acknowledgements. My objective is to be able to:
1. Loop through the script and get the lines where requests & their acknowledgements happen
2. Pull the entire lines of importance as strings and store them as variables for string modifying to output somewhere else.
Here's what I have so far:
#ECHO off
setlocal
setlocal enabledelayedexpansion
setlocal enableextensions
:: Lets get today's date, formatted the way the ABCD File is named
for /f "tokens=1-5 delims=/ " %%d in ("%date%") do set targetDate=%%f-%%d-%%e
:: Now we set the targetFile name
SET ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX*%targetDate%.log
::****Scrapped original approach*****
set "ackFoundCount=0"
set "reqFoundCount=0"
::Get lines with acks
for /f delims^=^ eol^= %%a in ('findstr /c:"\<ACK\>" "%ABCDLogsFile%"') do (
set /a "ackFoundCount+=1"
setlocal enabledelayedexpansion
for %%N in (!ackFoundCount!) do (
endlocal
set "ackFound%%N=%%a"
)
)
::Get lines with requests
for /f delims^=^ eol^= %%b in ('findstr /c:"ReqSingle" "%ABCDLogsFile%"') do (
set /a "reqFoundCount+=1"
setlocal enabledelayedexpansion
for %%N in (!reqFoundCount!) do (
endlocal
set "reqFound%%N=%%b"
)
)
setlocal enabledelayedexpansion
for /l %%N in (1,1,2 %reqFoundCount%) do echo REQ %%N FOUND= !reqFound%%N!
pause
for /l %%N in (1,1,2 %ackFoundCount%) do echo ACK %%N FOUND= !ackfound%%N!
endlocal
EDIT 2 dbenham
The roundabout way I was trying to accomplish this before was totally unnecessary.
Thanks to the questions and answer here:
'findstr' with multiple search results (BATCH)
I've got my script working similarly. However, I'm curious if its possible to get findstr output without the filepath at the beginning. I only need to substring out the timestamp in the log, which would always be the first 12 characters of each line (without the filepath). My output currently is prefixed with the path, and while I could get the path where the log would eventually be in production, it would be safer to try and do it another way. At the time that this script would eventually be run, there would only be 1 or 2 reqs and acks each, that is why I store all which are found. It's not necessary but I think it would be reassuring to see two if there are two. Here is what the output looks like for acks and reqs alike:
C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX 2015-04-01.log:2015-03-26 07:00:11,028 INFO etc...
I'm thinking that if I could strip the filepath off the start, then all I'd need to do to get just the timestamps of the events would be
for /l %%N in (1,1,1 %reqFoundCount%) do echo Req %%N occurred at: !reqFound%%N:~0,12! >> MorningAckChecks.txt
for /l %%N in (1,1,1 %ackFoundCount%) do echo ACK %%N occurred at: !ackfound%%N:~0,12! >> MorningAckChecks.txt
I suspect you could not get SKIP to work because you you were iterating the delimited list of line numbers with a FOR statement, which means the number is in a FOR variable. Problem is, you cannot include FOR variables or (delayed expansion) when specifying a SKIP value, or any other FOR option. The batch parser evaluates the FOR options before FOR variables are expanded, so it couldn't possibly work. Only normal expansion can be used when including a variable as part of FOR options.
But I don't understand why you think you need the line numbers at all. FINDSTR is already able to parse out the lines you want. Simply use FOR /F to iterate each matching line. For each line, define a variable containing the line content, and then use substring operations to parse out your desired values.
But I can offer an alternative that I think could make your life much easier. JREPL.BAT is a sophisticated regular expression text processor that could identify the lines and parse out and transform your desired values, all in one pass. JREPL.BAT is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward.
If I knew what your input looked like, and what your desired output is, then I could probably knock up a simple solution using JREPL.BAT. Or you could read the extensive built in documentation and figure it out for yourself.
Documentation is accessed from the command line via jrepl /?. You might want to pipe the output through MORE so you get one screen of help at a time. But I never do because my command line console is configured with a large output buffer, so I can simply scroll up to see past output.
EDIT - In response to comment and updated question
Here are the relevant snippets of your code that are causing the problem.
SET ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX*%targetDate%.log
findstr /c:"\<ACK\>" "%ABCDLogsFile%"
findstr /c:"ReqSingle" "%ABCDLogsFile%
The issue is your ABCDLogsFile definition includes a wildcard, which causes FINDSTR to prefix each matching line with the full path to the file name where the match occurred.
I have a simple solution for you - Just change the definition of ABCDLogsFile as follows:
SET "ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX<%targetDate%.log"
Explanation
My solution relies on two undocumented features
1) Undocumented file mask wildcards.
< - Very similar to *
> - Very similar to ?
These symbols are normally used for redirection, so they must be either quoted or escaped if you want to use them as file mask wildcards.
We discuss the undocumented feature at DosTips - Dir undocumented wildcards. Sprinkled throughout the thread (and a link) are some example use cases.
I document my understanding of exactly how the non-standard wildcards work at http://www.dostips.com/forum/viewtopic.php?p=39420#p39420
2) FINDSTR works with the non-standard wildcards
FINDSTR will prefix each matching line with the file name (and possibly path) if any of the following conditions occur
The /M option is used
The /F option is used
Multiple input files are explicitly listed on the command line
Multiple input files are implied via a file mask with at least one * or ? wildcard on the command line
Your are getting the file path prefix because of the last trigger - the * in your file mask.
But you can use < instead to get the same result, except the non-standard wildcards do not trigger the file prefix in the output.
Problem solved :-)
I talk about this FINDSTR feature at http://www.dostips.com/forum/viewtopic.php?p=39464#p39464.
Some day I hope to update my What are the undocumented features and limitations of the Windows FINDSTR command? post with this tasty little tidbit.
This post has become a bit cluttered. It would be very helpful if you posted the lines of input that correspond to the output you are getting. If you can't do that then add this statement before your FOR. I am sure you will find that testReqSkip is blank.
echo.testReqSkip=%testReqSkip%

CMD file - delayed variable expansion

I am writing a CMD/batch file (running under Win-7 cmd.exe) and I seem to be getting hung up on delayed variable expansion.
I am using text file input that is in the form:
9 .CN=ISRJX.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 8-20-13
10 .CN=FXXLISHER.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-13-13
11 .CN=QXX004F.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-14-13
12 *.CN=QXX1001OB.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-15-13
as contents in "inputfile.txt". Purpose is to extract the first word after ".CN=", at this point in the process.
Note that I can't strip based on number of chars before "CN=" because the number of chars will vary.
My script is:
setlocal enableextensions enableDelayedExpansion
REM #echo off
for /f "tokens=3 delims==." %%a in (inputfile.txt) do (
set "acct =%%a"
echo. %%a,%acct%
)
endlocal
I've tried every set of combination of quote, !, % etc. and both enabled & disabled delayed expansion, and still can't get this to work. For the most part, when I use ! I end up echoing the actual !. i.e. "echo !acct!" will echo the actual text "!acct!".
I have read many examples and answers, here and elsewhere, about delayed variable expansion. I just can't figure out how to work around it in this example, where I want "acct" to expand within the loop.
Suspect this is a simple punctuation problem, but I'm out of ideas. When I run this, I see acct set to the value for %%a, but when it echoes, clearly it doesn't expand to the new value -- instead it will echo whatever it was set to previously, or blank.
I have also tried disabledelayedexpansion, which makes no difference in my results (including when I use !acct! instead of %acct%.)
Remove the space after the acct variable name and use the ! marks.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /f "tokens=3 delims==." %%A in (inputfile.txt) do (
set "acct=%%A"
echo. %%A,!acct!
)
endlocal

Resources