Tham gia hai tệp, khớp trên một cột, với sự lặp lại


7

Làm thế nào tôi có thể nhận được hai tệp A và B, và đưa ra một kết quả như thế này:

Tập tin:

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

Tệp 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

Kết quả:

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

(tệp A: số phải khớp với số giữa từ tệp B)

Có cách nào có thể để làm điều này?

Câu trả lời:


5

Một giải pháp đơn giản với 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

Đây là một phiên bản nhận xét:

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

@ Jean, Cảm ơn bạn đã trả lời. :) Tôi đã có một kết quả tốt nhất từ ​​sự giúp đỡ của bạn.
JOSS

@JOSS, Nếu bạn chấp nhận awkcâu trả lời, bạn nên xóa thẻ bash-script.
Ricky Beam

3

Đây là một đoạn script Bash thực hiện những gì bạn đang tìm kiếm. Kịch bản được gọi 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

Khi bạn chạy nó:

$ ./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

Chi tiết

Điều đầu tiên chúng ta làm là sử dụng lệnh readarrayđể đọc nội dung của fileA.txtmột mảng. Đây là một tính năng mới hơn của Bash 4.x, vì vậy nếu bạn đang sử dụng phiên bản Bash cũ hơn, bạn có thể sử dụng một cái gì đó như thế này thay thế:

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

Phần còn lại của kịch bản này hơi phức tạp nhưng tôi đã để lại một đoạn dài echoở giữa mà bạn không thể bình luận để xem những gì đang diễn ra.

$ ./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

Những gì đang xảy ra ở đây? Có một bộ đếm, $imà chúng ta sử dụng để đếm từng dòng từ fileB.txtkhi chúng ta lặp qua nó. Sau đó, chúng tôi tính toán $idxbằng cách tính toán phân chia modulo của giá trị hiện tại $ivà số lượng dòng trong fileA.txt.

CHÚ THÍCH: chiều dài của mảng A. Bằng cách tính toán $idxtheo cách này, chúng tôi có thể làm cho nó "lặp lại" từ 0 đến 5, rồi 0 đến 5, v.v. Trong đầu ra gỡ lỗi ở trên, bạn có thể thấy điều này với IDX:cột.

Phần còn lại của tập lệnh khá chuẩn, sử dụng printfđể in các dòng được nối từ fileB.txtvới dòng tương ứng từ fileA.txt.


Cảm ơn bạn!! SIM, đó là những gì tôi cần cũng tìm hiểu thêm về nó
JOSS

@JOSS - Bạn khá hoan nghênh, cảm ơn vì Q thú vị!
slm

@SIM, tôi chỉ tìm ra kết quả không khớp .....
JOSS

1
tệp A: số phải khớp với số giữa từ tệp B. Bạn có biết cách khắc phục không ??
JOSS

2
$ mèo b | trong khi đọc b; do key = $ (echo $ b | awk '{in $ 3}'); / bin / echo -n "$ b"; grep -w $ khóa a | cắt -d \ -f2-; làm xong
01 tháng 12 năm 2013 01.664 001 AAA CAC 1083 Apple, CA
01 tháng 12 năm 2013 01.664 020 AAA CAC 0513 Chuối, CN
01 tháng 12 năm 2013 01.668 023 AAA CAC 1091 Apple, LA
01 tháng 12 năm 2013 01.668 101 AAA CAC 0183 Cam, HĐH
01 tháng 12 năm 2013 01.674 200 AAA CAC 0918 Kiwi, AA
01 tháng 12 năm 2013 01.674 045 AAA CAC 0918 Orange, TT
01 tháng 12 năm 2013 01.664 001 AAA CAC 2573 Apple, CA
01 tháng 12 năm 2013 01.668 101 AAA CAC 1091 Cam, HĐH
01 tháng 12 năm 2013 01.668 020 AAA CAC 6571 Chuối, CN
01 tháng 12 năm 2013 01.668 023 AAA CAC 2148 Apple, LA
01 tháng 12 năm 2013 01.674 200 AAA CAC 0918 Kiwi, AA
01 tháng 12 năm 2013 01.668 045 AAA CAC 5135 Cam, TT

Tôi nghi ngờ việc awkxây dựng có thể được thực hiện theo một cách thanh lịch hơn, nhưng nó dường như hoạt động.


cảm ơn, điều này làm việc Nhưng nó có thể sử dụng mảng?
JOSS

2

Các joinThực hiện tiện ích một "bình đẳng tham gia" trên các tập tin định và ghi kết quả vào đầu ra tiêu chuẩn. "Trường tham gia" là trường trong mỗi tệp mà các tệp được so sánh.

Nói cách khác, bạn có hai tệp chia sẻ một cột. Bạn có thể nối các dòng của các tệp trong đó cột bằng nhau.

Vì vậy hãy cố gắng:

$ 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

Đúng, làm việc. Nhưng không phải trong định dạng bạn chỉ định. Vì vậy, hãy trao đổi các tập tin:

$ 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

Tốt hơn nhiều. Vẫn không hoàn toàn đúng, vì trường tham gia xuất hiện đầu tiên. Awk có thể sửa nó lên:

$ 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

Vì vậy, có bạn đi. Các trường theo thứ tự tương tự. Trong awkbạn có thể sử dụng printfhoặc chèn một số tab nếu bạn muốn có khoảng cách chính xác, nhưng tôi nghĩ bạn sẽ có ý tưởng.


1
Lưu ý rằng bạn cần sắp xếp các tệp đầu vào trên trường tham gia joinđể hoạt động chính xác.
Stéphane Chazelas

1
joincũng không hoàn toàn đúng cho câu hỏi. Có nhiều dòng B hơn A; tham gia sẽ không xuất ra tất cả các dòng. Và nó phá hủy độ rộng trường cố định (tức là ăn khoảng trắng)
Ricky Beam

@RickyBeam - Sai. joinchắc chắn đúng công cụ cho công việc này: join -1 1 -2 3 -o 2.1 2.2 2.3 2.4 2.5 2.6 1.2 1.3 fileA <(sort -k3 fileB). Bạn thậm chí có thể giữ nguyên thứ tự các dòng trong fileBvà khoảng cách nếu bạn muốn, tìm kiếm các bài đăng của tôi dưới jointhẻ nếu bạn tò mò muốn xem làm thế nào.
don_crissti

Quan trọng: FILE1 và FILE2 phải được sắp xếp trên các trường tham gia. Điều đó khá vững chắc giết chết trật tự. joinlà một công cụ kém cho nhiệm vụ; bạn đã không chứng minh khác.
Ricky Beam

@RickyBeam - trong ví dụ OP file1đã được sắp xếp mặc dù nói chung cả hai đều phải được sắp xếp nên tôi hoàn toàn đồng ý ở đây. Thực tế là sort"kiên quyết giết chết thứ tự" là không liên quan vì bạn có thể sắp xếp lại đầu ra trở lại thứ tự ban đầu. Đó là, nếu bạn đủ thông minh. Tôi không cảm thấy cần phải chứng minh cho bạn bất cứ điều gì nhưng đây là một vài ví dụ để bạn đọc 1 , 2 , 3 .
don_crissti

0

Với một mảng, theo yêu cầu (hoàn toàn trong 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

(hoạt động trong bash v2)

Dòng đầu tiên tải mảng "A" từ tệp A. Bit 0x $ num là giữ mọi thứ trong cùng một cơ sở số nếu không các số 0 đứng đầu làm cho chúng trở thành bát phân. Dòng thứ hai đọc từng dòng của tệp B (giữ khoảng trắng), đặt các đối số vị trí từ dòng đó và cuối cùng in dòng cộng với mục được lập chỉ mục từ "A".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.