Объедините два файла с помощью 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.
Дэвид Фёрстер

@ Дэвид Это хороший момент. Лично мне очень не нравится создавать промежуточные файлы, но я также с нетерпением жду длительных процессов. Я задавался вопросом, каким будет «тривиальный размер», и поэтому сделал небольшой тест и добавил его к ответу вместе с вашим предложением.
JoL

Сортировать 1 млн записей достаточно быстро на достаточно современных настольных компьютерах. Еще на 2 порядка на 3 вещи становятся интереснее. В любом случае прошедшее (реальное) время ( %Eв формате времени) менее интересно измерить производительность вычислений. Процессорное время пользовательского режима ( %Uили просто неустановленная TIMEFORMATпеременная) было бы гораздо более значимым.
Дэвид Фёрстер

@ Дэвид Я не очень знаком с вариантами использования для разных времен. Почему это интереснее? Прошедшее время - это то, что совпадает со временем, которое я на самом деле жду. Для команды 1,5 секунды я получаю 4,5 секунды с %U.
JoL

1
На прошедшее время влияет время, потраченное на ожидание выполнения других задач в той же системе и блокирование запросов ввода-вывода. (Пользовательский) процессорного времени нет. Обычно при сравнении скорости алгоритмов с вычислительной привязкой желательно игнорировать ввод-вывод и избегать ошибок измерений из-за других фоновых задач. Важный вопрос: «Сколько вычислений требует этот алгоритм для этого набора данных?» вместо "Сколько времени мой компьютер потратил на все свои задачи, пока он ждал завершения этих вычислений?"
Дэвид Форстер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.