2017-02-08 10 views
1
cat input 

aaa paul peter 
bbb john mike 
ccc paul mike 
bbb paul john 

И мой словарь Dict файла:Заменить строку в соответствии файл словаря в AWK

cat dict 

aaa OOO 
bbb 111 
ccc 222 

мне нужно найти строку формы input и если матч первого столбца в файле dict, печать второй столбец файл формы dict к первому файл столбца input. Я могу использовать sub и gsub, но у меня есть тысяча строк в файле dict (с разными буквами).

cat output: 

000 paul peter 
111 john mike 
222 paul mike 
111 paul john 

Благодарим за помощь.

Мое решение:

awk: 

awk '{sub(/aaa/,"000",$1); sub(/bbb/,"111",$1); sub(/ccc/,"222",$1)1' input 

UPDATE:

Если не найдено совпадение с input в dict, держать слово в первом столбце без изменений.

вход кошка

aaa paul peter 
bbb john mike 
ccc paul mike 
bbb paul john 
ddd paul peter 

cat dict 

aaa OOO 
bbb 111 
ccc 222 

cat output: 

000 paul peter 
111 john mike 
222 paul mike 
111 paul john 
ddd paul peter 
+0

Как 'input' имеет две записи для' bbb', разве это не уникально? – Inian

+0

Ни одна запись в первом столбце не может повторяться много раз. Различия в других столбцах. – Geroge

+1

@fedorqui Я поставил свою попытку. Но эта работа, если у меня есть только замена нескольких слов. И я хотел бы прочитать из файла dict. Я не уверен, могу ли я использовать awk .. – Geroge

ответ

3

Более обобщенный подход, как было предложено fedorqui в комментариях для обработки несоответствие в названиях в input и dict файлов можно сделать что-то, как,

awk 'FNR==NR {dict[$1]=$2; next} {$1=($1 in dict) ? dict[$1] : $1}1' dict input 

Мое первоначальное решение ниже работы по делам при отсутствии пропущенных сопоставлений между input и dict файлами.

awk 'FNR==NR{hash[$2FS$3]=$1; next}{for (i in hash) if (match(hash[i],$1)){print $2, i} }' input dict 
OOO paul peter 
111 john mike 
111 paul john 
222 paul mike 

Идея заключается в том, чтобы создать хеш-карту с индексом, как $2FS$3 и значением как $1, т.е. hash["paul peter"]="aaa" и т.д. После этого строится, теперь файл словаря смотрел на, чтобы увидеть соответствующие строки из $1 в dict с хэш-значением от input. Если найдено совпадение, распечатайте содержимое по мере необходимости.

+0

Да, я имел в виду этот ответ. Использование полей 2 и 3 кажется немного ненужным. Кроме того, зачем сначала проходить вход, а затем через dict? Отображение выполняется через dict, поэтому оно должно быть загружено первым, а затем использовать его значения для изменения данных на входе. – fedorqui

+0

@fedorqui: Я использую этот подход для первого требования перед изменением, так случается, что я не делал его гибким для других случаев. Могу ли я использовать ваш комментарий в качестве обновления в своем ответе с должным кредитом? – Inian

+0

Я вижу. Поскольку он не является гибким и имеет большую сложность, чем простой «val in array» без какого-либо цикла, я думаю, что он выглядит ясным, что сначала разбор 'dict' должен быть самым простым способом! – fedorqui

1

Я думаю, что вы могли бы эффективно использовать GNU join:

sort input > sorted_input 
sort dict > sorted_dict 
join sorted_dict sorted_input -o 1.2,2.2,2.3 

Что дает следующий результат с вашими, например, данные (обратите внимание на то модифицированный вывод, но необходимо для join):

OOO paul peter 
111 john mike 
111 paul john 
222 paul mike 

Все это зависит от того, что поле объединения является первым из каждого файла, иначе вам нужно указать, в каком поле должны быть связаны файлы.

Параметр -o является формат вывод спецификации и относится к полям каждого файла, который мы хотим на выходе: второй область dict, а затем во всех областях, но первые из input.

Вы упомянули, что некоторые ключи могут быть найдены не в dict, и вы хотите сохранить значение из первого поля input. Есть опция -a, чтобы справиться с этим, но это будет бесполезно с нашим выходом, поэтому я думаю, что проще сделать два исполнения, первый из которых выводит строки с соответствием в каждом файле, а второй - обрабатывает строки без соответствия в dict:

$ join sorted_dict sorted_input -o 1.2,2.2,2.3; join sorted_dict sorted_input -v 2 
OOO paul peter 
111 john mike 
111 paul john 
222 paul mike 
ddd paul peter 

Если он добавляет слишком большую часть накладных расходов из-за размера файлов, вы должны вместо этого делать покадровую с -a 2 без вывода спецификации, а затем преобразовать результат с sed, awk или что-то еще обрабатывать строки с отсутствующим полем.

+0

Спасибо. Это работает, если у меня разное количество столбцов - например, ввод имеет 500 строк и dict имеет 50K строк? – Geroge

+0

строк или столбцов? С строками, да, это сработает; Я предполагаю, что он должен быть таким же эффективным, как любой скрипт 'awk', который вы можете написать, но вы обязательно должны его протестировать. С слишком большим количеством столбцов спецификация формата «-o» была бы докучливой для записи, и вместо этого я полагался бы на диапазоны 'cut' (например,' cut -d '' -f2-', который удалит только первое поле) – Aaron

+0

Ницца это выглядит, это работает .. Присоединяйтесь мощно! – Geroge

2

Изменено мой ответ:

awk 'NR==FNR{a[$1]=$2;next}{if ($1 in a)print a[$1],$2,$3; else print $0}' dict input 

гравюр

OOO paul peter 
111 john mike 
222 paul mike 
111 paul john 
ddd paul peter 

С помощью команды NR == FNR следующая команда только та или иная строчка на первый файл. Каждая строка хранится в массиве a с ключом $ 1 и значением $ 2. Затем $ 1 в a берет $ 1 из второго файла и смотрит, можно ли найти значение в массиве a. Если это правда, тогда [$ 1] печатает число и $ 2 и $ 3 имя. Теперь есть дополнительное предложение else, которое сохраняет отпечатки всей строки от ввода, если совпадение не найдено.

+0

Возможно, вы можете сказать '{$ 1 = ($ 1 в a)? dict [$ 1]: $ 1} 1', чтобы сделать его более кратким. – fedorqui

+2

@ Иниан: Я работал с 'awk 'FNR == NR {dict [$ 1] = $ 2; next} {$ 1 = ($ 1 в dict)? dict [$ 1]: $ 1} 1 'dict input', я думаю, что вы чрезмерны индексы. – fedorqui

+0

@fedorqui Это приятное улучшение. Спасибо ^^ – JFS31

0

awk был быстрее для операции, но вот чистое решение bash.

#!/bin/bash 

typeset -A dict 

function add_dict() 
{ 
    dict[$1]=$2 
} 

add_dict aaa 000 
add_dict bbb 111 
add_dict ccc 222 

while read row 
do 
    column=(${row//:/ }) 
    if [ "${dict[${column[0]}]}" ];then 
     echo ${dict[${column[0]}]} ${column[1]} ${column[2]} 
    else 
     echo ${column[0]} ${column[1]} ${column[2]} 
    fi 
done < /tmp/1M.txt 

#1 Million lines processed in 
#real 0m40.173s 
#user 0m37.668s 
#sys 0m2.462s 

#time awk 'NR==FNR{a[$1]=$2;next}{if ($1 in a)print a[$1],$2,$3; else print $0}' dict 1M.txt > processed.txt 

#real 0m0.281s 
#user 0m0.242s 
#sys 0m0.024s