2つの大きなファイルからn番目の列を繰り返し読み込み、並べて切り取り、貼り付けて新しいn番目のファイルを作成します。

2つの大きなファイルからn番目の列を繰り返し読み込み、並べて切り取り、貼り付けて新しいn番目のファイルを作成します。

2つのファイルがあり、それぞれ約100,000のスペースで区切られた列があります。両方のファイルの各列を抽出し、別々のファイルに書き込み、コマンド2を実行したいと思います。

2つの列を持つファイルの例。

cat test1.txt
rr1 rr2
1 2
1 2
1 1
2 1

cat test2.txt
rr1 rr2
2 2
1 1
2 1
2 2

test1.txtの最初の列とtest2.txtの最初の列を取得し、これら2つの列が並んでいる新しいファイルという新しいファイルを作成したいと思います。これまで試したコードは次のとおりです。

awk -F' ' '{
  for(i=1; i<=NF; i++){ # iterate over each column
    paste -d' ' <(sed 1d test1.txt | awk -v var1="$i" '{print $var1}') <(sed 1d test2.txt | awk -v var2="$i" '{print $var2}' ) > out$i
    # write to file named with column name or i
    # do command2 for out$1
  } 
}'  

ただし、このコードはエラーを発生させます。

awk: cmd. line:3:     paste -d
awk: cmd. line:3:             ^ unexpected newline or end of string

期待される出力

cat out1
1 2
1 1
1 2
2 2

cat out2
2 2
2 1
1 1
1 2

私はこれに多くの時間を費やしましたが、この問題を解決する方法がわかりません。どんな助けでも大変感謝します。もっと良い方法がありますか?

答え1

このようにしてくださいそしてツールボックスから列数と一般的なコマンドを取得します。

#!/bin/bash

for i in  $(seq 1 $(awk '{print NF;exit}' test1.txt)); do
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

または

#!/bin/bash

numcols=$(awk '{print NF;exit}' test1.txt)
for ((i=1; i<=numcols; i++)); do 
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

または使用:

#!/bin/ksh

numcols=$(awk '{print NF;exit}' test1.txt)
for i in  {1..$numcols}; do
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

それから:

cat out.1
cat out.2

コメントに記載されているように、あなたのようにawk混在させることはできませんshell

開発者でない場合は、ここで行ったように基本的なシェルコマンドを習得するのが最善です。

次の基本コマンドのドキュメントをお読みください。

  • tr
  • paste
  • sed
  • seq

それは基本的ではありません(ここでは簡単な方法で使用されます)。

  • awk

一時ファイル名で置換>(command ...)または置換を処理します。<(...)このファイルを書き込んだり読み込んだりすると、バイトは内部コマンドにパイプされます。通常はファイルリダイレクトと一緒に使用されますcmd1 2> >(cmd2)

望むより:
http://mywiki.wooledge.org/ProcessSubstitution
http://mywiki.wooledge.org/BashFAQ/024

答え2

仮定:

  • すべてのファイルには少なくとも1行(タイトル)があります。
  • すべてのファイルの行数は同じです。
  • すべてのファイルの列数は同じです。
  • awkすべてのファイルは(配列を介して)メモリに保存できます。

一般的なアプローチ:

  • 多次元配列を使用GNU awkできますが、副作用は(単一次元インデックスよりも)より多くのメモリを使用することです。
  • column # (NF)row number (FNR)インデックス++を使用してデータを1次元配列に格納します。file count
  • END{...}ブロックでは、データをout{1..NF}ファイルとして印刷する配列を繰り返します。

以下のみを使用してくださいawk

$ cat merge.awk

FNR==1 { fcnt++ }                                       # keep track of number of files
FNR>1  { for (i=1; i<=NF; i++)                          # loop through columns
             lines[i,FNR,fcnt]=$i                       # index = column # + row number + file count
       }
END    { for (i=1; i<=NF; i++) {                        # loop through columns
             for (j=2; j<=FNR; j++)                     # loop through rows
                 for (k=1; k<=fcnt; k++)                # loop through filecount
                     printf "%s%s", lines[i,j,k], (k<fcnt ? OFS : ORS), lines[i,j,k] > ("out" i)
             close ("out" i)
         }
       }

OPの2つのファイルに対して実行します。

$ awk -f merge.awk test1.txt test2.txt

$ head out?
==> out1 <==
1 2
1 1
1 2
2 2

==> out2 <==
2 2
2 1
1 1
1 2

3つの新しいファイル:

$ head t?.txt
==> t1.txt <==
rr1 rr2 rr3
1 2 3
4 5 6
7 8 9

==> t2.txt <==
rr1 rr2 rr3
a b c
d e f
g h i

==> t3.txt <==
rr1 rr2 rr3
X XX XXX
Y YY YYY
Z ZZ ZZZ

次の3つのファイルに対して実行します。

$ awk -f merge.awk t1.txt t2.txt t3.txt

$ head out?
==> out1 <==
1 a X
4 d Y
7 g Z

==> out2 <==
2 b XX
5 e YY
8 h ZZ

==> out3 <==
3 c XXX
6 f YYY
9 i ZZZ

答え3

このエラーは、一重引用符で囲まれた文字列内に一重引用符を使用できないことが原因で発生します。このawkコマンドは、プログラムをpaste -dプログラムawk(切り捨てによる構文エラーを含む)として処理し、残りのコード(引用符なしの空白まで)を処理する最初のファイル名などで処理します。また、シェルプログラム内ではコマンドを使用できませんawk

awk次のパイプラインは、使用されたコマンドに2つのファイルを並べて供給しますpaste。このawkコマンドは、各ファイルの列ペアを各列の別の出力ファイルに出力します。

$ paste test1.txt test2.txt | awk 'NR > 1 { for (i = 1; i <= NF/2; ++i) print $i, $(NF/2+i) >("out" i) }'
$ cat out1
1 2
1 1
1 2
2 2
$ cat out2
2 2
2 1
1 1
1 2

awk美しく印刷されたコード:

NR > 1 {
    for (i = 1; i <= NF/2; ++i)
        print $i, $(NF/2+i) > ("out" i)
}

NF/2最初の行に入力されたヘッダーを無視し、このコードはファイルの1つのフィールドを繰り返します(両方のファイルが同じ数のフィールドを持ち、2つのファイルのフィールドが同じ順序でペアでなければならないと仮定します)。フィールド、つまり私たちにフィールドの半分が与えられます。次に、フィールド番号の後に名前の付いたファイルにその番号を追加して、iその番号と一緒に最初のフィールドを印刷します。NF/2outi

わずかに変更するだけで、最初のファイルのヘッダーに基づいて出力ファイルの名前を指定できます(2番目のファイルのヘッダーは無視され、同じ順序であると仮定します)。

NR == 1 {
    for (i = 1; i <= NF/2; ++i) head[i] = $i
    next
}

{
    for (i = 1; i <= NF/2; ++i)
        print $i, $(NF/2+i) > head[i]
}

質問に提供されたデータに基づいて両方のファイルが生成されrr1rr2またはすでに存在する場合は上書きされます)。


以下のコメントで正しく指摘されているように(現在のコメントは削除されています)、上記の内容は100000列の「開いたファイルが多すぎます」エラーを引き起こす可能性があり、そのawk実装は開かれたファイル記述子プールをインテリジェントに管理しません(GNUのようにawk)。 。他のawk実装では、毎回出力ファイルを閉じますprint>>>

awk上記の最後のクリップを修正したバージョンは次のとおりです。

NR == 1 {
    for (i = 1; i <= NF/2; ++i) head[i] = $i
    next
}

{
    for (i = 1; i <= NF/2; ++i) {
        print $i, $(NF/2+i) >> head[i]
        close(head[i])
    }
}

答え4

col_co=$(awk 'END{print NF}' f1.txt)
for ((i=1;i<=$col_co;i++))
do 
awk -v i="$i" 'NR>1{print $i}' f1.txt|paste >file_1.txt
awk -v i="$i" 'NR>1{print $i}' f2.txt >file_2.txt
paste file_1.txt file_2.txt >out_new_$i.txt
done

出力

cat out_new_1.txt
1       2
1       1
1       2
2       2


cat out_new_2.txt
2       2
2       1
1       1
1       2

関連情報