For Loop in (Findstr *.txt) Output Concatenated Between Files - windows

I have a code to print all string from multi files with below code:
for /f %%A in ('findstr /R "[0-9]" *.txt') do echo %%A
However the output concatenated when switching to different files:
C:\Users>echoarray.cmd
File1.txt:123
File1.txt:321
File1.txt:312File2.txt:456
File2.txt:654
File2.txt:546File3.txt:789
File3.txt:678
File3.txt:777File4.txt:123
File4.txt:789
File4.txt:999
What I want to do is, to put sum of all integer per row (from *.txt) into array then print output into one text file:
(Found this from here thanks to Gerhard)
#echo off & setlocal enabledelayedexpansion
for /f "tokens=1* delims=:" %%a in ('findstr /R "[0-9]" *.txt') do (
set /a %%a+=1
set /a result[!%%a!]+=%%b
echo result[!%%a!] > result.txt
)
Example results would be like below (from output of echoarray.cmd above):
1491 ::my comment: first row result are from summation of 123+456+789+123
*continue for second row
*continue for third row
*continue for fourth row
I need your kind help to solve the concatenated output from findstr (hopefully someone can suggest me the correct solution for the array too).
P/S: I am using cmd on Windows 10

Perhaps something like this will satisfy your needs.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Delims==" %%G In ('"(Set Line[) 2>NUL"') Do Set "%%G="
Set "}=" & For /F "Tokens=1,* Delims=[]" %%G In ('
%SystemRoot%\System32\find.exe /N /V "" "*.txt" 2^>NUL ^|
%SystemRoot%\System32\findstr.exe /R "^\[[123456789][0123456789]*\][123456789][0123456789]*$"'
) Do Set /A Line[%%G] += %%H, } = %%G
If Not Defined } GoTo :EOF
(For /F "Tokens=1,* Delims==" %%G In ('"(Set Line[) 2>NUL"') Do Echo %%H) 1>"result.txt"
The above code uses find.exe to get the content of each .txt file, without tripping up on the lack of CRLF line endings. Then findstr.exe identifies only those lines which contain only a series of digits and passes those to the Do portion off the loop. Set /A then totals the content on a line number basis and prints to a .txt file only those results.
Please be aware that as this uses Set /A and has therefore ignored any lines where the first digit is a 0. This prevents possible issues with octal arithmetic. You should also be aware that due to your requested output strings, there is no way of you determining which totals belong to which lines, if any of your files contain some lines with non digit only characters in them.

Related

How can I use a batch file to find a string in a text file and replace the entire line while keeping blank lines [duplicate]

This DOS batch script is stripping out the blank lines and not showing the blank lines in the file even though I am using the TYPE.exe command to convert the file to make sure the file is ASCII so that the FIND command is compatible with the file. Can anyone tell me how to make this script include blank lines?
#ECHO off
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE.exe "build.properties" ^| FIND.exe /V ""`) DO (
ECHO --%%A--
)
pause
That is the designed behavior of FOR /F - it never returns blank lines. The work around is to use FIND or FINDSTR to prefix the line with the line number. If you can guarantee no lines start with the line number delimiter, then you simply set the appropriate delimiter and keep tokens 1* but use only the 2nd token.
::preserve blank lines using FIND, assume no line starts with ]
::long lines are truncated
for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B
::preserve blank lines using FINDSTR, assume no line starts with :
::long lines > 8191 bytes are lost
for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B
::FINDSTR variant that preserves long lines
type "file.txt" > "file.txt.tmp"
for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
del "file.txt.tmp"
I prefer FINDSTR - it is more reliable. For example, FIND can truncate long lines - FINDSTR does not as long as it reads directly from a file. FINDSTR does drop long lines when reading from stdin via pipe or redirection.
If the file may contain lines that start with the delimiter, then you need to preserve the entire line with the line number prefix, and then use search and replace to remove the line prefix. You probably want delayed expansion off when transferring the %%A to an environment variable, otherwise any ! will be corrupted. But later within the loop you need delayed expansion to do the search and replace.
::preserve blank lines using FIND, even if a line may start with ]
::long lines are truncated
for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
::preserve blank lines using FINDSTR, even if a line may start with :
::long lines >8191 bytes are truncated
for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
::FINDSTR variant that preserves long lines
type "file.txt" >"file.txt.tmp"
for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
del "file.txt.tmp"
If you don't need to worry about converting the file to ASCII, then it is more efficient to drop the pipe and let FIND or FINDSTR open the file specified as an argument, or via redirection.
There is another work around that completely bypasses FOR /F during the read process. It looks odd, but it is more efficient. There are no restrictions with using delayed expansion, but unfortunately it has other limitations.
1) lines must be terminated by <CR><LF> (this will not be a problem if you do the TYPE file conversion)
2) lines must be <= 1021 bytes long (disregarding the <CR><LF>)
3) any trailing control characters are stripped from each line.
4) it must read from a file - you can't use a pipe. So in your case you will need to use a temp file to do your to ASCII conversion.
setlocal enableDelayedExpansion
type "file.txt">"file.txt.tmp"
for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
<"file.txt.tmp" (
for /l %%N in (1 1 %cnt%) do(
set "ln="
set /p "ln="
echo(!ln!
)
)
del "file.txt.tmp"
I wrote a very simple program that may serve as replacement for FIND and FINDSTR commands when they are used for this purpose. My program is called PIPE.COM and it just insert a blank space in empty lines, so all the lines may be directly processed by FOR command with no further adjustments (as long as the inserted space don't cares). Here it is:
#ECHO off
if not exist pipe.com call :DefinePipe
FOR /F "USEBACKQ delims=" %%A IN (`pipe ^< "build.properties"`) DO (
ECHO(--%%A--
)
pause
goto :EOF
:DefinePipe
setlocal DisableDelayedExpansion
set pipe=´)€ì!Í!ŠÐŠà€Ä!€ü.t2€ü+u!:æu8²A€ê!´#€ì!Í!².€ê!´#€ì!Í!²+€ê!´#€ì!Í!Šò€Æ!´,€ì!Í!"Àu°´LÍ!ëÒ
setlocal EnableDelayedExpansion
echo !pipe!>pipe.com
exit /B
EDIT: Addendum as answer to new comment
The code at :DefinePipe subroutine create a 88 bytes program called pipe.com, that basically do a process equivalent to this pseudo-Batch code:
set "space= "
set line=
:nextChar
rem Read just ONE character
set /PC char=
if %char% neq %NewLine% (
rem Join new char to current line
set line=%line%%char%
) else (
rem End of line detected
if defined line (
rem Show current line
echo %line%
set line=
) else (
rem Empty line: change it by one space
echo %space%
)
)
goto nextChar
This way, empty lines in the input file are changed by lines with one space, so FOR /F command not longer omit they. This works "as long as the inserted space don't cares" as I said in my answer.
Note that the pipe.com program does not work in 64-bits Windows versions.
Antonio
Output lines including blank lines
Here's a method I developed for my own use.
Save the code as a batch file say, SHOWALL.BAT and pass the source file as a command line parameter.
Output can be redirected or piped.
#echo off
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" ^< "%~1"') do echo.%%ba
exit /b
EXAMPLES:
showall source.txt
showall source.txt >destination.txt
showall source.txt | FIND "string"
An oddity is the inclusion of the '^<' (redirection) as opposed to just doing the following:
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" "%~1"') do echo.%%ba
By omitting the redirection, a leading blank line is output.
Thanks to dbenham, this works, although it is slightly different than his suggestion:
::preserve blank lines using FIND, no limitations
for /f "USEBACKQ delims=" %%A in (`type "file.properties" ^| find /V /N ""`) do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
As mentioned in this answer to the above question, it doesn't seem that lines are skipped by default using for /f in (at least) Windows XP (Community - Please update this answer by testing the below batch commands on your version & service pack of Windows).
EDIT: Per Jeb's comment below, it seems that the ping command, in at least Windows XP, is
causing for /f to produce <CR>'s instead of blank lines (If someone knows specifically why, would
appreciate it if they could update this answer or comment).
As a workaround, it seems that the second default delimited token (<space> / %%b in the example)
returns as blank, which worked for my situation of eliminating the blank lines by way of an "parent"
if conditional on the second token at the start of the for /f, like this:
for /f "tokens=1,2*" %%a in ('ping -n 1 google.com') do (
if not "x%%b"=="x" (
{do things with non-blank lines}
)
)
Using the below code:
#echo off
systeminfo | findstr /b /c:"OS Name" /c:"OS Version"
echo.&echo.
ping -n 1 google.com
echo.&echo.
for /f %%a in ('ping -n 1 google.com') do ( echo "%%a" )
echo.&echo.&echo --------------&echo.&echo.
find /?
echo.&echo.
for /f %%a in ('find /?') do ( echo "%%a" )
echo.&echo.
pause
.... the following is what I see on Windows XP, Windows 7 and Windows 2008, being the only three versions & service packs of Windows I have ready access to:

How to add incrementing number prefix order by date on multiple files in Batch Script?

I'm using window OS, and I downloaded video as series lesson. I would like order those videos by date and adding prefix number for first video as 1 than 2, 3..... I found answer at here How to rename and add incrementing number suffix on multiple files in Batch Script? it can add number to filename, but can only order by name not date. Thank and sorry for my English.
Here is a simple implementation of what aschipfl suggested in his comment:
#echo off
setlocal enableDelayedExpansion
set /a n=0
for /f "delims=" %%F in ('dir /b /od') do (
set /a n+=1
ren "%%F" "!n!_%%F"
)
But there are two potential problems:
1) The FOR /F command has a default EOL character of ; - if a file name were to start with ;, then it would be skipped. The simple solution is to set EOL to a character that cannot appear in a file name - : is a good choice.
2) The rename will fail if a filename contains a ! character because delayed expansion is enabled, and %%F is expanded before delayed expansion. The solution is to save the file name to a variable, and then toggle delayed expansion ON and OFF within the loop
#echo off
setlocal disableDelayedExpansion
set /a n=0
for /f "eol=: delims=" %%F in ('dir /b /od') do (
set "file=%%F"
set /a n+=1
setlocal enableDelayedExpansion
ren "!file!" "!n!_!file!"
endlocal
)
There is a simpler option - use FINDSTR /N to prefix the DIR /B /OD output with the line number, and then use FOR /F to parse out the number and the file name. Now there is no need for a counter variable or delayed expansion.
#echo off
for /f "delims=: tokens=1*" %%A in ('dir /b /od ^| findstr /n "^"') do ren "%%B" "%%A_%%B"
You don't even need a batch file. You could use the following directly from the command line:
for /f "delims=: tokens=1*" %A in ('dir /b /od ^| findstr /n "^"') do ren "%B" "%A_%B"

How to display a window of lines in a .txt file in batch?

So I'm building a messaging program in batch (I know, it's newbish) and the program takes user input, puts it in my .txt file log.txt, and types it on the screen. I want the output to look like this...
Title
----------------------
contents
of
the
file
here
----------------------
User input here>>
This may seem simple, but the file will be constantly updated by users and I want the program to only display a range of lines to keep that message area stays the same size. I found a simple program to display specific lines, but I can't make them move down one line each time log.txt is changed. Here it is:
#setlocal enableextensions enabledelayedexpansion
#echo off
set lines=1
set curr=1
for /f "delims=" %%a in ('type bob.txt') do (
for %%b in (!lines!) do (
if !curr!==%%b echo %%a
)
set /a "curr = curr + 1"
)
endlocal
(By the way, this program is called lines.bat. I just call it in cmd to test it.)
To return a defined number of lines starting from a certain line number, you can do the following:
#echo off
setlocal EnableExtensions
rem define the (path to the) text file here:
set "TEXT_FILE=log.txt"
rem define the line number here:
set /A "LINE_NUMBER=1"
rem define the number of lines here:
set /A "LINE_COUNT=5"
set /A "LINE_LIMIT=LINE_NUMBER+LINE_COUNT-1"
for /F delims^=^ eol^= %%L in ('findstr /N /R "^" "%TEXT_FILE%"') do (
setlocal DisableDelayedExpansion
set "LINE=%%L"
setlocal EnableDelayedExpansion
for /F "tokens=1 delims=:" %%N in ("!LINE!") do set "LNUM=%%N"
set "LINE=!LINE:*:=!"
if !LNUM! GEQ !LINE_NUMBER! (
if !LNUM! LEQ !LINE_LIMIT! (
echo !LINE!
)
)
endlocal
endlocal
)
endlocal
The findstr command with the /R "^" search pattern returns all lines. The findstr switch /N lets every line precede with a line number (starting from 1) and a colon. The : is used to split the line in two parts: the first part representing the line number is checked whether it is in the range to be returned; the second part is the original line of text which is simply output in case. Even empty lines are taken into account.
You might ask why not simply using the above mentioned : as a delims delimiter option for for /F, but this would cause problems with lines of text starting with :.
The toggling of delayed expansion is necessary to avoid trouble with special characters like !, for instance.
To return the last defined number of lines, the following approach can be used:
#echo off
setlocal EnableExtensions
rem define the (path to the) text file here:
set "TEXT_FILE=log.txt"
rem define the number of lines here:
set /A "LINE_COUNT=5"
for /F "tokens=1 delims=:" %%L in ('findstr /N /R "^" "%TEXT_FILE%"') do (
set /A "LINE_SKIP=%%L"
)
set /A "LINE_SKIP-=LINE_COUNT"
if %LINE_SKIP% GTR 0 (
set "LINE_SKIP=skip^=%LINE_SKIP%^ "
) else (
set "LINE_SKIP="
)
for /F %LINE_SKIP%delims^=^ eol^= %%L in ('findstr /N /R "^" "%TEXT_FILE%"') do (
setlocal DisableDelayedExpansion
set "LINE=%%L"
setlocal EnableDelayedExpansion
set "LINE=!LINE:*:=!"
echo !LINE!
endlocal
endlocal
)
endlocal
Again, the findstr /N /R "^" command is used. But here, we have an additional for /F loop first, which merely counts the number of lines in the text file, extracting the line number preceded by findstr. The second for /F loop is quite similar to the above approach, but a dynamic skip option is introduced, so that the loop starts iterating through the last lines only; the rest is almost the same as above, except that the conditions concerning the current line number have been removed.
I know I could do the counting of lines also by using find /C /V "" rather than looping through the findstr /N /R "^" output, but if there are one or more empty lines at the end of the file, find returns a number one less as the findstr method, so I went for findstr consistently.
Also here, delayed expansion is toggled to avoid trouble with the ! character.

Count the number of lines of each text file in a given directory and store it in a variable

I want to count the number of lines of each text file in a given directory and store them in a variable.
Here is my code:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "temp\textpipe_tmp\" %%U in (*.txt) DO (
set "cmd=findstr /R /N "^^" "%%U" | find /C ":""
for /f %%a in ('!cmd!') do set number=%%a
echo %number%
)
:eof
pause
I'm not sure why it does not work but if I get rid of SET, it works:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "temp\textpipe_tmp\" %%U in (*.txt) DO (
findstr /R /N "^" "%%U" | find /C ":"
)
:eof
pause
I need the result stored in a variable.
Another version, which does the same thing but is slightly better readable:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "C:\Users\Gebruiker\Documents\ICT" %%U in (*.txt) DO (
set lines=0
for /f %%A in (%%U) do (set /a lines+=1)
echo !lines!
)
pause
As #wOxxOm stated in his comment, find is the perfect choice for this task.
Supposing there is a file test.txt containing 12 lines, find /V /C "" "C:test.txt" will output something like:
---------- C:TEST.TXT: 12
So let us use a for /F loop to capture such an output and string substitution to get the text portion after :SPACE:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R "temp\textpipe_tmp\" %%U in ("*.txt") do (
rem capturing the output of `find` here:
for /F "delims=" %%A in ('find /V /C "" "%%~U"') do (
set "NUMBER=%%~A"
rem substituting substring `: ` and everything before by nothing:
set "NUMBER=!NUMBER:*: =!"
)
rem at this point, variable `NUMBER` is available
rem for the currently processed file in `%%~U`:
echo !NUMBER!
)
endlocal
Note that find /V /C "" will return unexpected reslts if there are empty lines at the end of the file (one of such might not be included in the count). However, empty lines at the beginning or in between non-empty ones will be counted.
Update:
Using redirection like > "C:test.txt" find /V /C "" rather than find /V /C "" "C:test.txt" avoids the prefix ---------- C:TEST.TXT: and just returns the number of lines (like 12). With this modification no string substitution is necessary and so the code looks like this:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R "temp\textpipe_tmp\" %%U in ("*.txt") do (
rem capturing the output of `find` here:
for /F "delims=" %%A in ('^> "%%~U" find /V /C ""') do (
set "NUMBER=%%~A"
)
rem at this point, variable `NUMBER` is available
rem for the currently processed file in `%%~U`:
echo !NUMBER!
)
endlocal
The redirection mark < needs to be escaped like ^< when being used after in in for /F.

Batch script skipping blank entries in a .CSV when delim is ','

I have a .CSV that I am trying to sort through to create another file from the data, but when I run it through, it skips blank entries. For example, if a line is
value,value,value,,,value
and I try to get the 4th column, it would spit out 6th. Presumably because it is the next valid value. I don't want it to skip the blank entry as it can mess up the tables I'm trying to make. Anyone know how to resolve this? (Any tips are welcome as I suck at batch scripts)
Here is my script:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10,11,12,13,14 delims=," %%a in (file.csv) DO (
echo %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n
)
pause
This is the standard behaviour of the FOR/F loop, consecutive delims only used as one delimiter.
But you can use a workaround with a second FOR/F.
Prefix each column with another character, split the line at the delim and remove the prefix.
setlocal EnableDelayedExpansion
FOR /F "delims=" %%L in (test.bat) DO (
set "line=%%L,,,,,,,,"
set "line=#!line:,=,#!"
FOR /F "tokens=1,2,3,4 delims=," %%a in ("!line!") DO (
set "param1=%%a"
set "param2=%%b"
set "param3=%%c"
set "param4=%%d"
set "param1=!param1:~1!"
set "param2=!param2:~1!"
set "param3=!param3:~1!"
set "param4=!param4:~1!"
echo !param1! !param2! !param3! !param4!
)
)
As jeb already mentiones in his answer, for /F treats consecutive delimiters as one. To avoid that, you could replace each delimiter , by "," and enclose each full line by "", so each field appears as being enclosed within "", which can easily be removed finally by the ~ modifier of the for /F variable; so there is no need to do any more string manipulations (like sub-string expansion) later on:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "usebackq delims=" %%# in ("file.csv") do (
set "LINE=%%#"
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=," %%A in (^""!LINE:,="^,"!"^") do (
endlocal
echo Field 1: %%~A
echo Field 2: %%~B
echo Field 3: %%~C
echo Field 4: %%~D
setlocal EnableDelayedExpansion
)
endlocal
)
endlocal
This might not work properly if the data contain , characters by themselves (remember that , is not considered as delimiter in case a field is enclosed within "" in the original CSV file).
The toggling of delayed expansion is done to not lose any exclamation marks in the data.

Resources