user278064
user278064

Reputation: 10170

Windows batch: Iterate over mulitple files lines at the same time

Is it possible to iterate over the lines of multiple files at the same time? I need to output a file by joining the lines in two different files (and add some extra words).

I know the CMD command:

FOR %variable IN (set) DO command [command-parameters]

But that does not help if you need to iterate over two files at the same time. More precisely, I need to the following:

While the first file is not ended
   line1 <- READ a line from the first file
   line2 <- READ a line from the second file
   WRITE on a third file line1 + line2

I'm looking for a way to accomplish what I'vve previously describing in a dos batch file. Thanks!

Upvotes: 3

Views: 2398

Answers (4)

bpatrao
bpatrao

Reputation: 460

I had to do this for 2 strings (might come in handy for someone else)

@echo off
setlocal enableextensions enabledelayedexpansion

set string1=token1,token2,token3
set string2=2ndtoken1,2ndtoken2,2ndtoken3

for %%A in (%string1%) do (
    echo From String1: %%A

    set str2=
    for %%S in (!string2!) do (
        if not defined str2 (
            set str2=%%S

            echo !str2!%>tempfile.txt
            FOR %%? IN (tempfile.txt) DO ( SET /A strlength=%%~z? - 1 )

            REM echo StrLen=!strlength!
            echo From String2: !str2!

            call set string2=%%string2:~!strlength!%%
            REM echo String2: !string2!
        )
    )
)

Upvotes: 0

dbenham
dbenham

Reputation: 130829

I know of 3 basic techniques, from which a large number of hybrids could be derived.

Option 1 (this option is very similar to the wmz solution)

Read file 1 with a FOR /F and read file 2 with a second FOR /F with a SKIP option. File 2 must be read in a CALLed subroutine so that the loop can be broken without breaking the file 1 loop.

Limitations:

  • Blank lines will cause lines to get out of synch. Blank lines are included in the skip count but are not read by FOR /F.
  • Lines beginning with ; will not be read because of the default EOL option. This can be solved by setting EOL to a new line character if necessary. See HOW TO: FOR /F Disabling EOF or using a quote as delim
  • Lines are limited to a maximum length of 8191 bytes.

This option is slow because of the CALL and because the 2nd file must be read once for each line.

EDIT - code fixed to still output a line if 2nd file ends early

@echo off
setlocal disableDelayedExpansion
set "file1=a.txt"
set "file2=b.txt"
set "out=out.txt"

set /a cnt=0
set "skip="

>"%out%" (
  for /f "usebackq delims=" %%A in ("%file1%") do (
    set "found="
    call :readFile2
    if not defined found (echo %%A - )
  )
)
type "%out%"
exit /b

:readFile2
for /f "usebackq %skip% delims=" %%B in ("%file2%") do (
  set found=1
  echo %%A - %%B"
  goto :break
)
:break
set /a cnt+=1
set "skip=skip=%cnt%"
exit /b


Option 2

This option solves the blank line problem by using FINDSTR to prefix each line with the line number followed by a colon. FINDSTR is used to read only the nth line from file 2, so no need to break out of the 2nd loop.

Limitations:

  • Leading colons will be stripped from the lines. This limitation can be eliminated with extra code, but it will make it more complicated and slower.
  • Lines are limited to a maximum length of 8191 bytes.

This option is even slower than option 1

EDIT - code fixed to still output a line if 2nd file ends early

@echo off
setlocal disableDelayedExpansion
set "file1=a.txt"
set "file2=b.txt"
set "out=out.txt"

>"%file2%.tmp" findstr /n "^" "%file2%"
>"%out%" (
  for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "%file1%"') do (
    set "found="
    for /f "tokens=1* delims=:" %%a in ('findstr "^%%A:" "%file2%.tmp"') do (
      set found=1
      echo %%B - %%b
    )
    if not defined found (echo %%B - )
  )
)
del "%file2%.tmp"
type "%out%"


Option 3

Use SET /P to read both files. FIND is used to get a count of the number of lines in file 1 because SET /P cannot tell the difference between a blank line and end of file.

This option eliminates a lot of limitations and complexity, but introduces its own limitations.

Limitations:

  • Lines must use Windows style line terminators of <CR><LF>. Unix style <LF> will not work.
  • lines are limited to 1021 bytes
  • trailing control characters are stripped from each line.

This option is by far the fastest. It is preferred as long as the limitations are acceptable.

@echo off
setlocal enableDelayedExpansion
set "file1=a.txt"
set "file2=b.txt"
set "out=out.txt"

for /f %%N in ('type "%file1%"^|find /c /v ""') do set "cnt=%%N"
>"%out%" 9<"%file1%" <"%file2%" (
  for /l %%N in (1 1 %cnt%) do (
    set "ln1="
    set "ln2="
    <&9 set /p "ln1="
    set /p "ln2="
    echo !ln1! - !ln2!
  )
)
type "%out%"

Upvotes: 8

wmz
wmz

Reputation: 3685

and now I have to deliver :-)

@echo off
setlocal disabledelayedexpansion
set count=0
for /f "delims=" %%F in (outer.txt) do (
  set fline=%%F
  call :inner %%count%%
  call echo %%fline%%
  set /a count+=1  

)
goto :eof

:inner
if %1 neq 0 set skipline=skip=%1 
  for /f "%skipline% delims=" %%S  in (inner.txt) do (
    set fline=%%F%%S
    goto :eof
  )

Please note it may fail for some types of input, eg. when lines contain & or some other characters which are treated specially by the shell

Upvotes: 2

EthanB
EthanB

Reputation: 4289

Appending file content:

echo FOO > foo.txt
echo BAR > bar.txt
type foo.txt > both.txt
echo "Both" >> both.txt
type bar.txt >> both.txt

Upvotes: 0

Related Questions