Поєднайте два файли з awk


9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Потрібний вихід:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Як я можу це зробити?

Відповіді:


11

Нижче наведена відповідь ґрунтується на аналогічному запитанні в питаннях SO із деякими відповідними модифікаціями:

$ 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

Ідея полягає у створенні хеш-карти з покажчиком та використанні її як словника.

Для другого питання, яке ви задали у своєму коментарі ( що слід змінити, якщо другий стовпець file1.txtбуде шостим стовпцем ):

Якщо вхідний файл буде таким file1b.txt:

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

Наступна команда виконає це:

$ 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 - я оновив свою відповідь, щоб також вирішити ваше друге питання в коментарі. Якщо я відповів на ваше запитання, прийміть його.
Ярон

6

Я знаю, ви сказали awk, але для цього є joinкоманда ...

{
  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

Було б достатньо для першої joinкоманди, якби не цей рядок:

item4   platD

Команда в основному говорить: приєднайтеся на основі другого стовпця першого файлу ( -1 2) та першого стовпця другого файлу ( -2 1) та виведіть перший стовпець першого файлу та другий стовпець другого файлу ( -o 1.1,2.2). Це показує лише лінії, які спарені. Друга команда приєднання говорить майже те саме, але вона говорить про те, щоб показати рядки з першого файлу, які неможливо було спарити ( -v 1), та вивести перший стовпець першого файлу та другий стовпець першого файлу ( -o 1.1,1.2). Потім сортуємо вихід обох комбінованих. sort -k 1означає сортувати на основі першого стовпця, а sort -k 2означає сортувати на основі другого стовпця . Важливо сортувати файли на основі стовпця об'єднання, перш ніж передавати їх join.

Тепер я написав сортування двічі, тому що мені не подобається засмічувати свої каталоги файлами, якщо я можу в цьому допомогти. Однак, як сказав Девід Фоерстер, залежно від розміру файлів, ви, можливо, захочете сортувати файли та зберегти їх спочатку, щоб не чекати, щоб сортувати кожен двічі. Щоб дати уявлення про розміри, ось час, який потрібен для сортування 1 мільйона та 10 мільйонів рядків на моєму комп’ютері:

$ 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

Це 1,5 секунди на 1 мільйон рядків і 19 секунд на 10 мільйонів рядків.


У цьому випадку було б краще зберігати відсортовані вхідні дані у (тимчасових) проміжних файлах, оскільки сортування займає досить тривалий набір даних нетривіального розміру. Інакше +1.
Девід Фоерстер

@David Це хороший момент. Особисто мені дуже не подобається створювати проміжні файли, але я також нетерплячий до тривалих процесів. Мені було цікаво, що буде "тривіально розміром", і тому я зробив невеликий орієнтир і додав його до відповіді разом із вашою пропозицією.
JoL

Сортування 1-мільйонних записів досить швидко на досить сучасних настільних комп’ютерах. З 2 ще 3 порядками більше речей починає ставати цікавішими. У будь-якому випадку минулий (реальний) час ( %Eформат часу) менш цікавий для вимірювання обчислювальної ефективності. Час процесорного режиму в режимі користувача ( %Uабо просто встановлена TIMEFORMATзмінна) буде набагато більш значущим.
Девід Фоерстер

@David Я не дуже знайомий із випадками використання для різних часів. Чому це цікавіше? Минулий час - це те, що збігається з часом, який я насправді чекаю. Для команди 1,5 секунди я отримую 4,5 секунди %U.
JoL

1
На минулий час впливає час, витрачений на очікування інших завдань, що працює в тій же системі, і блокування запитів вводу / виводу. (Користувач) Час процесора - ні. Зазвичай, порівнюючи швидкість обчислювально пов'язаних алгоритмів, потрібно нехтувати введенням-виведенням і уникати помилок вимірювань через інші фонові завдання. Важливе питання: "Скільки потрібно для цього алгоритму для цього набору даних?" замість "Скільки часу мій комп'ютер витратив на всі свої завдання, поки він чекав завершення цього обчислення?"
Девід Фоерстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.