Kết hợp hai tập tin với awk


9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Đầu ra mong muốn:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Tôi làm nó như thế nào?

Câu trả lời:


11

Câu trả lời dưới đây dựa trên một câu hỏi và trả lời tương tự trong SO với một số sửa đổi có liên quan:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($2 in dict) ? dict[$2] : $2}1' file2.txt file1.txt 
item1 platA
item2 platB
item3 platC
item4 platD
item5 platE

Ý tưởng là tạo ra một bản đồ băm với chỉ mục và sử dụng nó làm từ điển.

Đối với câu hỏi thứ 2 bạn đã hỏi trong nhận xét của mình ( điều gì sẽ được thay đổi nếu cột thứ hai file1.txtsẽ là cột thứ sáu ):

Nếu tệp đầu vào sẽ như file1b.txtsau:

item1 A5 B C D carA
item2 A4 1 2 3 carB
item3 A3 2 3 4 carC
item4 A2 4 5 6 platD
item5 A1 7 8 9 carE

Lệnh sau sẽ thực hiện:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($6 in dict) ? dict[$6] : $6;$3="";$4="";$5="";$6=""}1' file2.txt file1b.txt 
item1 platA    
item2 platB    
item3 platC    
item4 platD    
item5 platE    

1
@pawana - Tôi đã cập nhật câu trả lời của mình để giải quyết câu hỏi thứ hai của bạn trong bình luận. Nếu tôi đã trả lời câu hỏi của bạn xin vui lòng chấp nhận nó.
Yaron

6

Tôi biết bạn đã nói awk, nhưng có một joinlệnh cho mục đích này ...

{
  join -o 1.1,2.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt)     
  join -v 1 -o 1.1,1.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt) 
} | sort -k 1

Nó là đủ với joinlệnh đầu tiên nếu nó không dành cho dòng này:

item4   platD

Về cơ bản lệnh nói: tham gia dựa trên cột thứ hai của tệp đầu tiên ( -1 2) và cột đầu tiên của tệp thứ hai ( -2 1) và xuất cột đầu tiên của tệp đầu tiên và cột thứ hai của tệp thứ hai ( -o 1.1,2.2). Điều đó chỉ hiển thị các dòng mà ghép nối. Lệnh tham gia thứ hai nói gần như giống nhau, nhưng nó cho biết hiển thị các dòng từ tệp đầu tiên không thể ghép nối ( -v 1) và xuất cột đầu tiên của tệp đầu tiên và cột thứ hai của tệp đầu tiên ( -o 1.1,1.2). Sau đó, chúng tôi sắp xếp đầu ra của cả hai kết hợp. sort -k 1có nghĩa là sắp xếp dựa trên cột đầu tiên và sort -k 2có nghĩa là sắp xếp dựa trên cột thứ hai. Điều quan trọng là sắp xếp các tệp dựa trên cột tham gia trước khi chuyển chúng đến join.

Bây giờ, tôi đã viết cách sắp xếp hai lần, vì tôi không muốn xả các thư mục của mình bằng các tệp nếu tôi có thể giúp nó. Tuy nhiên, như David Foerster đã nói, tùy thuộc vào kích thước của các tệp, bạn có thể muốn sắp xếp các tệp và lưu chúng trước để không phải chờ đợi để sắp xếp hai lần. Để đưa ra ý tưởng về kích thước, đây là thời gian để sắp xếp 1 triệu và 10 triệu dòng trên máy tính của tôi:

$ ruby -e '(1..1000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 1million.txt 
$ ruby -e '(1..10000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 10million.txt 
$ head 10million.txt 
item530284   plat530284
item7946579   plat7946579
item1521735   plat1521735
item9762844   plat9762844
item2289811   plat2289811
item6878181   plat6878181
item7957075   plat7957075
item2527811   plat2527811
item5940907   plat5940907
item3289494   plat3289494
$ TIMEFORMAT=%E
$ time sort 1million.txt >/dev/null
1.547
$ time sort 10million.txt >/dev/null
19.187

Đó là 1,5 giây cho 1 triệu dòng và 19 giây cho 10 triệu dòng.


Trong trường hợp này, tốt hơn là lưu trữ dữ liệu đầu vào được sắp xếp trong các tệp trung gian (tạm thời) vì việc sắp xếp mất khá nhiều thời gian cho các tập dữ liệu có kích thước không tầm thường. Nếu không +1.
David Foerster

@David Đó là một điểm tốt. Cá nhân, tôi thực sự không thích phải tạo các tệp trung gian, nhưng tôi cũng không kiên nhẫn với các quy trình chạy dài. Tôi tự hỏi "kích cỡ tầm thường" sẽ là gì, và vì vậy tôi đã làm một điểm chuẩn nhỏ, và thêm nó vào câu trả lời cùng với gợi ý của bạn.
JoL

Để sắp xếp các bản ghi 1 mio là đủ nhanh trên các máy tính để bàn hiện đại hợp lý. Với 2 hơn 3 đơn hàng độ lớn, nhiều thứ bắt đầu trở nên thú vị. Trong mọi trường hợp, thời gian trôi qua (thực) %Eở định dạng thời gian) ít thú vị hơn để đo hiệu suất tính toán. Thời gian CPU của người dùng ( %Uhoặc đơn giản là một TIMEFORMATbiến không đặt ) sẽ có ý nghĩa hơn nhiều.
David Foerster

@David Tôi không thực sự quen thuộc với các trường hợp sử dụng cho các thời điểm khác nhau. Tại sao nó thú vị hơn? Thời gian trôi qua là những gì trùng khớp với thời gian mà tôi thực sự chờ đợi. Đối với lệnh 1,5 giây, tôi nhận được 4,5 giây với %U.
JoL

1
Thời gian đã qua bị ảnh hưởng bởi thời gian chờ đợi cho các tác vụ khác đang chạy trên cùng hệ thống và chặn các yêu cầu I / O. (Người dùng) Thời gian CPU thì không. Thông thường khi so sánh tốc độ của các thuật toán ràng buộc tính toán, người ta muốn bỏ qua I / O và tránh các lỗi đo do các tác vụ nền khác. Câu hỏi quan trọng là "Thuật toán này yêu cầu bao nhiêu tính toán trên tập dữ liệu đó?" thay vì "Máy tính của tôi đã dành bao nhiêu thời gian cho tất cả các nhiệm vụ của nó trong khi nó chờ tính toán đó hoàn thành?"
David Foerster
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.