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

Как мне это сделать?

pawana
источник

Ответы:

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
На прошедшее время влияет время, потраченное на ожидание выполнения других задач в той же системе и блокирование запросов ввода-вывода. (Пользовательский) процессорного времени нет. Обычно при сравнении скорости алгоритмов с вычислительной привязкой желательно игнорировать ввод-вывод и избегать ошибок измерений из-за других фоновых задач. Важный вопрос: «Сколько вычислений требует этот алгоритм для этого набора данных?» вместо "Сколько времени мой компьютер потратил на все свои задачи, пока он ждал завершения этих вычислений?"
Дэвид Форстер