web-dev-qa-db-ja.com

2つのファイルを結合し、列を照合して繰り返します

2つのファイルAとBを取得し、次のような結果を出力するにはどうすればよいですか。

ファイルA:

001 Apple, CA
020 Banana, CN
023 Apple, LA
045 Orange, TT
101 Orange, OS
200 Kiwi, AA

ファイルB:

01-Dec-2013 01.664  001     AAA CAC 1083
01-Dec-2013 01.664  020     AAA CAC 0513
01-Dec-2013 01.668  023     AAA CAC 1091
01-Dec-2013 01.668  101     AAA CAC 0183
01-Dec-2013 01.674  200     AAA CAC 0918
01-Dec-2013 01.674  045     AAA CAC 0918
01-Dec-2013 01.664  001     AAA CAC 2573
01-Dec-2013 01.668  101     AAA CAC 1091
01-Dec-2013 01.668  020     AAA CAC 6571
01-Dec-2013 01.668  023     AAA CAC 2148
01-Dec-2013 01.674  200     AAA CAC 0918
01-Dec-2013 01.668  045     AAA CAC 5135

結果:

01-Dec-2013 01.664  001     AAA CAC 1083    Apple, CA
01-Dec-2013 01.664  020     AAA CAC 0513    Banana, CN
01-Dec-2013 01.668  023     AAA CAC 1091    Apple, LA
01-Dec-2013 01.668  101     AAA CAC 0183    Orange, OS
01-Dec-2013 01.674  200     AAA CAC 0918    Kiwi, AA
01-Dec-2013 01.674  045     AAA CAC 0918    Orange, TT
01-Dec-2013 01.664  001     AAA CAC 2573    Apple, CA
01-Dec-2013 01.668  101     AAA CAC 1091    Orange, OS
01-Dec-2013 01.668  020     AAA CAC 6571    Banana, CN
01-Dec-2013 01.668  023     AAA CAC 2148    Apple, LA
01-Dec-2013 01.674  200     AAA CAC 0918    Kiwi, AA
01-Dec-2013 01.668  045     AAA CAC 5135    Orange, TT

(ファイルA:番号はファイルBの中央の番号と一致する必要があります)

これを行うための可能な方法はありますか?

7
JOSS

Awkを使用した簡単なソリューション:

awk -v FILE_A="file-A" -v OFS="\t" 'BEGIN { while ( ( getline < FILE_A ) > 0 ) { VAL = $0 ; sub( /^[^ ]+ /, "", VAL ) ; DICT[ $1 ] = VAL } } { print $0, DICT[ $3 ] }' file-B

コメント付きのバージョンは次のとおりです。

awk -v FILE_A="file-A" -v OFS="\t" '
BEGIN {

  # Loop on the content of file-A
  # to put the values in a table

  while ( ( getline < FILE_A ) > 0 ){

     # Remove the index from the value
     VAL = $0
     sub( /^[^ ]+ /, "", VAL )

     # Fill the table
     DICT[ $1 ] = VAL
  }
}
{

  # Print the line followed by the
  # corresponding value
  print $0, DICT[ $3 ]

}' file-B
5
user20877

これがあなたが探していることをするBashスクリプトです。スクリプトはmergeAB.bashと呼ばれます。

#!/bin/bash

readarray A < fileA.txt 

i=0
while read -r B; do
  idx=$(( $i % ${#A[@]} ))

  printf "%s %s" "$B" "${A[$idx]}"
  #echo "i: $i | A#: ${#A[@]} | IDX: $idx"

  let i=i+1
done < fileB.txt

あなたがそれを実行するとき:

$ ./mergeAB.bash 
01-Dec-2013 01.664  001     AAA CAC 1083 001 Apple, CA
01-Dec-2013 01.664  020     AAA CAC 0513 020 Banana, CN
01-Dec-2013 01.668  023     AAA CAC 1091 023 Apple, LA
01-Dec-2013 01.668  101     AAA CAC 0183 045 Orange, TT
01-Dec-2013 01.674  200     AAA CAC 0918 101 Orange, OS
01-Dec-2013 01.674  045     AAA CAC 0918 200 Kiwi, AA
01-Dec-2013 01.664  001     AAA CAC 2573 001 Apple, CA
01-Dec-2013 01.668  101     AAA CAC 1091 020 Banana, CN
01-Dec-2013 01.668  020     AAA CAC 6571 023 Apple, LA
01-Dec-2013 01.668  023     AAA CAC 2148 045 Orange, TT
01-Dec-2013 01.674  200     AAA CAC 0918 101 Orange, OS
01-Dec-2013 01.668  045     AAA CAC 5135 200 Kiwi, AA

詳細

最初に行うことは、コマンドreadarrayを使用して、fileA.txtの内容を配列に読み込むことです。これはBash4.xの新しい機能であるため、古いバージョンのBashを使用している場合は、代わりに次のようなものを使用できます。

$ IFS=$'\n' read -d '' -r -a A < fileA.txt

このスクリプトの残りの部分は少し複雑ですが、コメントを外して何が起こっているかを確認できるように、途中に冗長なechoを残しました。

$ ./mergeAB.bash | grep i:
i: 0 | A#: 6 | IDX: 0
i: 1 | A#: 6 | IDX: 1
i: 2 | A#: 6 | IDX: 2
i: 3 | A#: 6 | IDX: 3
i: 4 | A#: 6 | IDX: 4
i: 5 | A#: 6 | IDX: 5
i: 6 | A#: 6 | IDX: 0
i: 7 | A#: 6 | IDX: 1
i: 8 | A#: 6 | IDX: 2
i: 9 | A#: 6 | IDX: 3
i: 10 | A#: 6 | IDX: 4
i: 11 | A#: 6 | IDX: 5

何が起きてる? $iからの各行をループするときにカウントするために使用するカウンターfileB.txtがあります。次に、$idxの現在の値と$iの行数のモジュロ除算を計算することにより、fileA.txtを計算します。

注:配列の長さA。この方法で$idxを計算することにより、0から5、次に0から5などに「ループ」させることができます。上記のデバッグ出力では、IDX:列でこれを確認できます。

スクリプトの残りの部分はかなり標準的であり、printfを使用して、fileB.txtからの連結行とfileA.txtからの対応する行を出力します。

3
slm

joinユーティリティは、指定されたファイルに対して「等式結合」を実行し、結果を標準出力に書き込みます。 「結合フィールド」は、ファイルが比較される各ファイルのフィールドです。

つまり、列を共有する2つのファイルがあります。列が等しいファイルの行を結合できます。

では、試してみましょう。

$ join -1 1 -2 3 a b
001 Apple, CA 01-Dec-2013 01.664 AAA CAC 1083
020 Banana, CN 01-Dec-2013 01.664 AAA CAC 0513
023 Apple, LA 01-Dec-2013 01.668 AAA CAC 1091
101 Orange, OS 01-Dec-2013 01.668 AAA CAC 0183
200 Kiwi, AA 01-Dec-2013 01.674 AAA CAC 0918

うん、動作します。ただし、指定した形式ではありません。それでは、ファイルを交換しましょう。

$ join -1 3 -2 1 b a
001 01-Dec-2013 01.664 AAA CAC 1083 Apple, CA
020 01-Dec-2013 01.664 AAA CAC 0513 Banana, CN
023 01-Dec-2013 01.668 AAA CAC 1091 Apple, LA
101 01-Dec-2013 01.668 AAA CAC 0183 Orange, OS
200 01-Dec-2013 01.674 AAA CAC 0918 Kiwi, AA

ずっといい。結合されたフィールドが最初に表示されるため、まだ完全には正しくありません。 Awkはそれを修正することができます:

$ join -1 3 -2 1 b a | awk '{print $2,$3,$1,$4,$5,$6,$7,$8}'
01-Dec-2013 01.664 001 AAA CAC 1083 Apple, CA
01-Dec-2013 01.664 020 AAA CAC 0513 Banana, CN
01-Dec-2013 01.668 023 AAA CAC 1091 Apple, LA
01-Dec-2013 01.668 101 AAA CAC 0183 Orange, OS
01-Dec-2013 01.674 200 AAA CAC 0918 Kiwi, AA

だからあなたは行き​​ます。フィールドは同じ順序です。 awkでは、printfを使用するか、正確な間隔を取得したい場合はいくつかのタブを挿入できますが、アイデアは得られると思います。

2
bahamat
 $ cat b |読みながらb; do key = $(echo $ b | awk '{print $ 3}');/bin/echo -n "$ b"; grep -w $ key a |カット-d\-f2-;完了
 01-Dec-2013 01.664 001 AAA CAC 1083 Apple、CA 
 01-Dec-2013 01.664 020 AAA CAC 0513 Banana、CN 
 01-Dec-2013 01.668 023 AAA CAC 1091 Apple、LA 
 01-Dec-2013 01.668 101 AAA CAC 0183 Orange、OS 
 01-Dec-2013 01.674 200 AAA CAC 0918 Kiwi、AA 
 01-Dec- 2013 01.674 045 AAA CAC 0918 Orange、TT 
 01-Dec-2013 01.664 001 AAA CAC 2573 Apple、CA 
 01-Dec-2013 01.668 101 AAA CAC 1091 Orange、OS 
 2013年12月1日01.668020 AAA CAC 6571バナナ、CN 
 2013年12月1日01.668023 AAA CAC 2148アップル、LA 
 2013年12月1日01.674200 AAA CAC 0918キウイ、AA 
 01-Dec-2013 01.668 045 AAA CAC 5135 Orange、TT 

awk構文はも​​っとエレガントな方法で実行できると思いますが、うまくいくようです。

2
ckujau

配列を使用して、要求に応じて(完全にbashに)...

while read num loc; do A[0x$num]=$loc; done < A
while read B; do set -- $B; echo "${B} ${A[0x$3]}"; done < B

(bash v2で動作します)

最初の行は、ファイルAから配列 "A"をロードします。x $ numビットは、すべてを同じ基数に保つことです。そうでない場合、先行ゼロはそれらを8進数にします。 2行目は、ファイルBの各行を読み取り(スペースを保持)、その行から位置引数を設定し、最後にその行と「A」からのインデックス付きエントリを出力します。

0
Ricky Beam