开发者

Batch: combine txt files in columns

开发者 https://www.devze.com 2023-04-07 00:22 出处:网络
I am trying to merge txt files (from which first column is equal in all) into one file. Thousands of them that I acquire in my experiments. I use 3 input files as an example to illustrate what I am tr

I am trying to merge txt files (from which first column is equal in all) into one file. Thousands of them that I acquire in my experiments. I use 3 input files as an example to illustrate what I am trying to achieve:

1.txt     2.txt     3.txt
l1 a1     l1 b1     l1 c1
l2 a2     l2 b2     l2 c2
l3 a3     l3 b3     l3 c3

So all input files have the first column in common. My wish is to get this output:

out.txt
l1 a1 b1 c1
l2 a2 b2 c2
l3 a3 b3 c3

Attempt 1 of 3

:: hmm.bat=============================================
@echo off > hmm.txt & setLocal enableDELAYedeXpansion
echo AAAAAAAAAAaaaaaaaaaa............
pushd %*

for /f "tokens=1* delims= " %%a in (a1.txt) do (
>> hmm.txt echo. %%a hm
)

for %%j in (*.txt) do (
   echo. %%j yes? >> hmm.txt
   for /f "tokens=2* delims= " %%a in (%%j) do (
   >> hmm.txt echo. %%a
   )
)
popd
:: End_Of_Batch======================================

This batch file does extract the columns I want, but rather than that everything is in separate columns, all data is in one single column. I have not been able to manage to get output into separate columns.

This attempt (2 of 3) would ultimately give what I want:

::=============================================
@echo off > tral.txt & setLocal enableDELAYedeXpansion

set N=
for /f "tokens=1* delims= " %%a in (a1.txt) do (
set /a N+=1 & call :sub1 %%a & set A!N!=!C!
)

set N=
for /f "tokens=* delims= " %%a in (a1.txt) do (
set /a N+=1 & call :sub1 %%a & set B!N!=!C!
)

set N=
for /f "tokens=* delims= " %%a in (a2.txt) do (
set /a N+=1 & call :sub1 %%a & set C!N!=!C!
)

set N=
for /f "tokens=* delims= " %%a in (a3.txt) do (
set /a N+=1 & call :sub1 %%a & set D!N!=!C!
)

for /L %%a in (1 1 !N!) do (
>> tral.txt echo. !A%%a! !B%%a! !C%%a! !D%%a!
)
goto :eof

:sub1 set C to last token
:loop
if '%2' neq '' (
shift
goto :loop
)
set C=%1
goto :eof
::================================================

However, to extend this to many (thousands) files, I would need to repeat the bit

set N=
for /f "tokens=* delims= " %%a in (a*.txt) do (
set /a N+=1 & call :sub1 %%a & set x!N!=!C!
)

maaaaaaaany times. I have tried using a loop instead:

Attempt 3 of 3

::=============================================
@echo off & setLocal enableDELAYedeXpansion
type nul > slow.txt

set N=
for /f "tokens=1* delims= " %%a in (a1.txt) do (
set /a N+=1 & cal开发者_运维技巧l :sub1 %%a & set A!N!=!C!
)

for %%j in (*.txt) do (
set N=
for /f "tokens=* delims= " %%a in (%%j) do (
set /a N+=1 & call :sub1 %%a & set x!N!=!C!
)
)

for /L %%a in (1 1 !N!) do (
>> slow.txt echo. !A%%a! !x%%a!
)
goto :eof

:sub1 set C to last token
:loop
if '%2' neq '' (
shift
goto :loop
)
set C=%1
goto :eof
::=============================================

I end up with the first column (which all data files have in common), and the second column of the very last data file. I do not know how to update the variable x in !x%%a! for each file, to get this printed in a separate column.

Alternatively, does anyone know if it is possible to echo data at the end of a chosen line in the output file? I would then echo it to the end of the first line, which then would result in echoing all data in columns. Using

set /P line1=< hmm.txt

and then

echo.%line1% %%a>>hmm.txt

does not result in echoing at the end of the first line, but rather at the end of the last line.

Anyone has a solution either way?


In the meantime I have managed to find the solution (with much help from others on http://www.computing.net/answers/programming/merge-files-in-column-output/26553.html).

Perhaps some day someone else has a nice use for this too.

First it counts the files (nr of files is used later on). The files names without extension are listed in a file "list.dat".

@echo off 
SetLocal EnableDelayedExpansion
for /f  %%a in ('dir/b *.txt') do (
    set /a count+=1
    set /a count1=count-1
)
echo total is !count!
echo !count1!

rem empty contents of files
type nul > x.txt
type nul > y.txt
type nul > z.txt
type nul > list.dat

setlocal
set first=y
(
    for /f %%g in ('dir/b/a-d a*.txt') do (
        if defined first (
            set first=
            set/p=%%~ng <nul
        ) else (
            set/p=%%~ng <nul
        )
    )
)>>list.dat

for /f %%j in (list.dat) do (
    echo. %%j   
    for %%a in (%%j) do find /n /v "" < %%a.txt >> x.txt
)
sort x.txt /o x.txt

set "regex=^\[[0-9]\]"
:loop
findstr /r "%regex%" x.txt >> y.txt
if not errorlevel 1 (
    set "regex=^\[[0-9]%regex:~3%
    goto loop
)

set cnt=
set line=
for /f "delims=" %%a in (y.txt) do (
    set input=%%a
    set input=!input:*]=!
    set line=!line! !input!
    if "!cnt!"=="!count1!" (
        >> z.txt echo !line:~1!
        set line=
    )
    set /a cnt=^(cnt + 1^) %% !count!
)

type nul > zz.dat
for /F "delims=" %%i in (z.txt) do (
    set cnt=1
    set rrow=
    for %%j in (%%i) do (
        set /A cnt-=1
        if !cnt! equ 0 (set cnt=0 & set rrow=!rrow! %%j)
    )
    set cnt=2
    set row=
    for %%j in (%%i) do (
        set /A cnt-=1
        if !cnt! equ 0 (set cnt=2 & set row=!row! %%j)
    )
    set row=!row:~1!
    echo. !rrow! !row!>> zz.dat
)

del x.txt
del y.txt
pause


I don't think BAT programming is appropiate for what you want to acomplish. You might find some convoluted solution, or end up using a third party tool to complement BAT programming (SED comes immediately to my mind) would IMO only add another problem to the equation, either forcing the user to install such a tool, or complicating your BAT distribution and installation with the same intent.

On the other hand, solving your problem is a very easy task to program in any general purpouse programming language.

So, I would try either VB or javascript, which are available in almost all windows installations. Take a look at this and get started...

@set @junk=1 /*
@echo off
rem textcolumns text
cscript //nologo //E:jscript %0 %*
goto :eof
:allfiles
pushd %1
for /f %%a in ('dir /b /s *.txt') do
  call :coltxt %%a
)
goto :eof
:coltxt
cscript //nologo //E:jscript %*
goto :eof
*/


function openFile(fn) {
 var ForReading = 1, ForWriting = 2, ForAppending = 8;
 var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
 var fso = new ActiveXObject("Scripting.FileSystemObject");
 // Create the file, and obtain a file object for the file.
 // fso.CreateTextFile(fn);
 var f = fso.GetFile(fn);
 // Open a text stream for input.
 ts = f.OpenAsTextStream(ForReading, TristateUseDefault);
 // Read from the text stream and display the results.
 return ts;
}


function removeFirstWord(s) {
  var p1=s.indexOf(" ");
  if (p1>0) {
    return s.substring(p1+1);
  } else {
    return new String();
  }
}

function getCol(ts) {
 var s;
 s = ts.ReadLine();
 s = removeFirstWord(s);
 return s;
}


// main 
x = WScript.Arguments;
fs = new Array(x.length);

for (var i=0; i<x.length; i++) {
 fs[i] = openFile(x(i));
}

while (!fs[0].AtEndOfStream) {
  var s = fs[0].ReadLine();
  for (var i=1; i<fs.length; i++) {
    s += getCol(fs[i]);
  }
  WScript.echo(s);
}

for (var i=0; i<fs.length; i++) {
  fs[i].close();
}


If all files have same length:

for /f %i in ('find /c /v "" ^< 1.txt') do set n=%i

To find number of lines.

Set number of files:

set f=4

Then read & merge each line:

for /l %b in (1,1,%n%) do (
set /p=l%b <nul
for /l %a in (1,1,%f%) do (
for /f "tokens=2" %i in ('find "l%b" ^< %a.txt') do (
set /p=%i <nul
))
echo:
)

Sample output:

l1 a1 b1 c1 d1
l2 a2 b2 c2 d2
l3 a3 b3 c3 d3

Tested on Win 10 CMD

Batch: combine txt files in columns

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号