J2897
J2897

Reputation: 19

Batch FOR LOOP a line of text from each file and ECHO both lines

OS: Windows 7

I need to output two lines; one from each text file, side by side. Similar to what has been shown here.

A.txt;

1
2
3
4
5

B.txt;

A
B
C
D
E

So I would like to echo;

1 A
2 B
3 C
4 D
5 E

Upvotes: 0

Views: 3466

Answers (4)

Mofi
Mofi

Reputation: 49086

The Windows Command Processor cmd.exe processing a batch file is the worst choice for such a text files merging task as it is designed for executing commands and executables and not for processing text files.

But this text lines merging task can be done with a batch file if the files two merge are not too large and the lines are not too long which means the line output by command ECHO must have less than 8192 characters not counting the newline characters carriage return and line-feed.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileA=%~dp0A.txt"
set "FileB=%~dp0B.txt"
set "ResultsFile=%~dp0Merged AB.txt"
if not exist "%FileA%" echo ERROR: Missing file: "%FileA%"& exit /B
if not exist "%FileB%" echo ERROR: Missing file: "%FileB%"& exit /B
set "FileALinesCount=0"
set "FileBLinesCount=0"
for /F "delims==" %%I in ('set Line 2^>nul') do set "%%I="
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /R /N "^" "%FileA%"') do for /F delims^=:^ eol^= %%J in ("%%I") do set "LineA%%J=%%I" & set "FileALinesCount=%%J"
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /R /N "^" "%FileB%"') do for /F delims^=:^ eol^= %%J in ("%%I") do set "LineB%%J=%%I" & set "FileBLinesCount=%%J"
if %FileALinesCount% GEQ %FileBLinesCount% (set "LinesCount=%FileALinesCount%") else set "LinesCount=%FileBLinesCount%"
if %LinesCount% == 0 del "%ResultsFile%" 2>nul & exit /B
setlocal EnableDelayedExpansion
(for /L %%I in (1,1,%LinesCount%) do (
    if defined LineA%%I (
        if defined LineB%%I (
            echo(!LineA%%I:*:=! !LineB%%I:*:=!
        ) else echo(!LineA%%I:*:=!
    ) else echo(!LineB%%I:*:=!
))>"%ResultsFile%"
endlocal
endlocal

This batch file first defines the required execution environment by disabling command echo mode, enabling command extensions and disabling delayed environment variable expansion.

Next is checked if the files A.txt and B.txt exist both in the directory of the batch file otherwise an error message is output and the batch file processing ends.

The first FOR loop deletes all environment variables which are by chance already defined with a name starting with Line.

The second FOR loop runs one more command process in background with %ComSpec% /c and the FINDSTR command line appended as additional arguments which just outputs all lines with line number and colon of file A.txt. The output lines are captured by FOR and assigned completely to loop variable I. For each line one more FOR is used to get the line number left to the colon and define next an environment variable with name LineA and the line number appended with the entire line output by FINDSTR assigned as value. The current line number is additionally assigned to the environment variable FileALinesCount.

The third FOR loop does the same for all lines in file B.txt.

The number of lines of each file could be different. For that reason the IF condition finds out what is the greatest lines count value and assigns it to the environment variable LinesCount.

The results file is deleted on already existing (from a previous execution of the batch file) on file A.txt and B.txt both exist, but are both empty files.

Next one more FOR loop is used now in an environment with enabled delayed environment variable expansion to output the lines from the two files merged together on one line with using a space character between them. In case of file A.txt has more lines than file B.txt, then just the line of file A.txt is output if there is no more line from file B.txt. In case of file B.txt has more lines than file A.txt, then just the line of file B.txt is output if there is no more line from file A.txt. The output lines are written into the results files as defined at top of the batch file. The line number and the colon added by FINDSTR on output of all lines to standard output stream of background command process are removed during the output of the lines by using a string substitution.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • call /? ... explains %~dp0 ... drive and path of argument 0 which is the batch file path
  • del /?
  • echo /?
  • endlocal /?
  • exit /?
  • findstr /?
  • for /?
  • if /?
  • set /?
  • setlocal /?

See also: How to read and print contents of text file line by line?


Some additional information about the line length limitations can be read on the Microsoft documentation pages:

The argument string of command SET or command ECHO cannot have more than 8191 characters.

In the batch file code above the argument string of command SET is everything after set and the space character separating the command from its argument, i.e. the quotation mark ", the variable name, the equal sign =, the string value and once again the quotation mark ".

The argument string of command ECHO is in the batch file everything after the command echo and left parenthesis ( used as separator instead of a space character to output correct also an empty line or a line starting with /?.

Here is an example to demonstrate the command line argument(s) string length limitation of 8191 characters.

There is a batch file LengthTest.cmd with the following command lines:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "InputFile=%~dp0Length Test Input.txt"
if not exist "%InputFile%" exit /B
set "OutputFile=%~dp0Length Test Output"
del "%OutputFile% *.txt" 2>nul
(for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I
    set "Line=%%I"
    echo(!Line!
))>"%OutputFile% 1_1.txt"
(for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I
    set Line=%%I
    echo(!Line!
))>"%OutputFile% 2_1.txt"
(for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I
    set L=%%I
    echo(!L!
))>"%OutputFile% 3_1.txt"
(for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I
    set L=%%I
    echo(!L! !L!
))>"%OutputFile% 4_1.txt"
(for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I
))>"%OutputFile% 5_1.txt"
for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I>>"%OutputFile% 1_2.txt"
    set "Line=%%I"
    echo(!Line!>>"%OutputFile% 1_2.txt"
)
for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I>>"%OutputFile% 2_2.txt"
    set Line=%%I
    echo(!Line!>>"%OutputFile% 2_2.txt"
)
for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I>>"%OutputFile% 3_2.txt"
    set L=%%I
    echo(!L!>>"%OutputFile% 3_2.txt"
)
for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I>>"%OutputFile% 4_2.txt"
    set L=%%I
    echo(!L! !L!>>"%OutputFile% 4_2.txt"
)
for /F "usebackq delims=" %%I in ("%InputFile%") do (
    echo(%%I>>"%OutputFile% 5_2.txt"
)
endlocal

In the directory of this batch file is the text file Length Test Input.txt which has six lines ending all with carriage return and line-feed. The six lines are:

Line 1 - 8184 chars: 0123456789...0123456789abc
Line 2 - 8186 chars: 0123456789...0123456789abcde
Line 3 - 8189 chars: 0123456789...0123456789abcdefgh
Line 4 - 8190 chars: 0123456789...0123456789abcdefghi
Line 5 - 8191 chars: 0123456789...0123456789abcdefghij
Line 6 - 8192 chars: 0123456789...0123456789abcdefghijk

... is used here as a placeholder for 814 repetitions of 0123456789.

The execution of this batch file produces ten files.

  1. Length Test Output 1_1.txt and Length Test Output 1_2.txt contain:

    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 1 - 8184 chars: 0123456789...0123456789abc
    
  2. Length Test Output 2_1.txt and Length Test Output 2_2.txt contain:

    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    
  3. Length Test Output 3_1.txt and Length Test Output 3_2.txt contain:

    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    Line 3 - 8189 chars: 0123456789...0123456789abcdefgh
    Line 3 - 8189 chars: 0123456789...0123456789abcdefgh
    
  4. Length Test Output 4_1.txt and Length Test Output 4_2.txt contain:

    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    Line 3 - 8189 chars: 0123456789...0123456789abcdefgh
    
  5. Length Test Output 5_1.txt and Length Test Output 5_2.txt contain:

    Line 1 - 8184 chars: 0123456789...0123456789abc
    Line 2 - 8186 chars: 0123456789...0123456789abcde
    Line 3 - 8189 chars: 0123456789...0123456789abcdefgh
    Line 4 - 8190 chars: 0123456789...0123456789abcdefghi
    Line 5 - 8191 chars: 0123456789...0123456789abcdefghij
    

Conclusion:

  1. The sixth line with 8192 characters is too long for being processed at all by the Windows command processor.
  2. It does not make a difference for the contents of the created files if everything output with ECHO inside a FOR loop is first captured by cmd.exe and then written into a file (*_1.txt files) or each output is directly written into a file (*_2.txt files) on processing small file(s).
  3. The output of a line with command ECHO with more than 8191 characters fails as demonstrated by echo(!L! !L!. The two created output files Length Test Output 4_1.txt and Length Test Output 4_2.txt contain only the lines output with echo(%%I in the two loops.
  4. A FOR loop with command SET on which the argument string to process by SET has more than 8191 characters results in an exit of the loop before executing any command in body of the loop. I am quite sure that this behavior of the Windows command processor is definitely not expected by the vast majority of batch file programmers.

The batch file LengthTest.cmd produces the same ten files on Windows XP SP3, Windows 7 SP1 and Windows 10 1909.

Upvotes: 1

captcha
captcha

Reputation: 3756

Code for GNU :

sed "s#.*#s/.*/& \&/g;n#" A.txt|sed -f - B.txt

>sed "s#.*#s/.*/& \&/g;n#" A.txt|sed -f - B.txt
1 A
2 B
3 C
4 D
5 E

Upvotes: 0

Monacraft
Monacraft

Reputation: 6630

Try this:

@echo off
setlocal enabledelayedexpansion
set /a count=0

For /f %%a in (A.txt) do (
set /a count=!count!+1
set A!count!=%%a
)
set count=0
For /f %%b in (B.txt) do (
set /a count=!count!+1
set B!count!=%%b
)
set recount=0
:loop
recount=%recount%+1
echo %A!recount!% %B!recount!%
if %recount% gtr %count% goto :end
goto :recount
:end
pause
exit

Hope this helped,

Yours Mona

Note : If there are more lines in A.txt then it will ignore them. If there are less, then it will show [blank] [line from B.txt]

Upvotes: 0

foxidrive
foxidrive

Reputation: 41224

I had this squirrled away which gives you a technique to solve that.

@echo off
del file1.txt 2>nul
del file2.txt 2>nul

for %%a in (A B C D) do echo %%a>>file2.txt
for %%a in (1 2 3 4) do echo %%a>>file1.txt

@echo off
setlocal DisableDelayedExpansion
< file2.txt (
   for /F "delims=" %%a in (file1.txt) do (
      set file2Line=
      set /P file2Line=
      set "file1Line=%%a"
      setlocal EnableDelayedExpansion   
      echo(!file1Line! !file2Line!
      endlocal
   )
)
pause

del file1.txt 2>nul
del file2.txt 2>nul
goto :EOF

Upvotes: 2

Related Questions