Найти часть строки в CSV и заменить целую ячейку на новую запись?

У меня есть файл CSV со столбцом, который я хочу просеять. Я хочу использовать файл шаблона, чтобы найти все записи, в которых шаблон существует даже в части значения столбца, и заменить все значение ячейки на этот «шаблон». Я составил список ключевых слов, которые я хочу использовать в качестве своего «шаблонного» банка; Итак, если ячейка в этом столбце (в этом случае только вторая) имеет этот «шаблон» как часть своей строки, я хочу заменить целую ячейку этим «шаблоном».

так например:

мой целевой файл:

id1,Taxidermy Equipment & Supplies,moreinfo1 id2,Taxis & Private Hire,moreinfo2 id3,Tax Services,moreinfo3 id4,Tools & Hardware,moreinfo4 id5,Tool Sharpening,moreinfo5 id6,Tool Shops,moreinfo6 id7,Video Conferencing,moreinfo7 id8,Video & DVD Shops,moreinfo8 id9,Woodworking Equipment & Supplies,moreinfo9 

мой «шаблонный» файл:

 Taxidermy Equipment & Supplies Taxis Tax Services Tool Video Wood 

выходной файл:

 id1,Taxidermy Equipment & Supplies,moreinfo1 id2,Taxis,moreinfo2 id3,Tax Services,moreinfo3 id4,Tool,moreinfo4 id5,Tool,moreinfo5 id6,Tool,moreinfo6 id7,Video,moreinfo7 id8,Video,moreinfo8 id9,Wood,moreinfo9 

Я придумал обычную «найти и заменить» sed:

 sed -i 's/PATTERN/REPLACE/g' file.csv 

но я хочу, чтобы он работал в определенном столбце, поэтому я придумал:

 awk 'BEGIN{OFS=FS="|"}$2==PATTERN{$2=REPLACE}{print}' file.csv 

но он не работает на «части строки» ([Видео]: «Видео и DVD-магазины» -> «Видео»), и я не могу понять, как awk принимает ввод как файл для «Шаблона», блок.

Есть ли awk-скрипт для этого? Или мне нужно что-то написать (например, в python со встроенным csv-костюмом?)

В awk, используя index . Он только печатает запись, если выполняется замена, но ее легко модифицировать, даже если нет совпадения (например, заменить print $1,i,$3} на $0=$1 OFS i OFS $3} 1 ):

 $ awk -F, -v OFS=, ' NR==FNR { a[$1]; next } # store "patterns" to a arr { for(i in a) # go thru whole a for each record if(index($2,i)) # if "pattern" matches $2 print $1,i,$3 # print with replacement } ' pattern_file target_file id1,Taxidermy Equipment & Supplies,moreinfo1 id2,Taxis,moreinfo2 id3,Tax Services,moreinfo3 id4,Tool,moreinfo4 id5,Tool,moreinfo5 id6,Tool,moreinfo6 id7,Video,moreinfo7 id8,Video,moreinfo8 id9,Wood,moreinfo9 

Perl, используя Text :: CSV_XS :

 #!/usr/bin/perl use warnings; use strict; use Text::CSV_XS qw{ csv }; my ($input_file, $pattern_file) = @ARGV; open my $pfh, '<', $pattern_file or die $!; chomp( my @patterns = <$pfh> ); my $aoa = csv(in => $input_file); for my $line (@$aoa) { for my $pattern (@patterns) { if (-1 != index $line->[1], $pattern) { $line->[1] = $pattern; last } } } csv(in => $aoa, quote_space => 0, eol => "\n", out => \*STDOUT); 

Вот (в основном) awk-решение:

 #/bin/bash patterns_regex=`cat patterns_file | tr '\n' '|'` cat target_file | awk -F"," -v patterns="$patterns_regex" ' BEGIN { OFS=","; split(patterns, patterns_split, "|"); } { for (pattern_num in patterns_split) { pattern=patterns_split[pattern_num]; if (pattern != "" && $2 ~ pattern) { print $1,pattern,$3 } } }' 

Когда вы захотите решить эту проблему с помощью sed , вам понадобятся некоторые шаги.
Для каждого шаблона вам понадобится команда типа

 sed 's/^\([^,]*\),\(.*Tool.*\),/\1,Tool,/' inputfile 

Вам понадобится каждый шаблон дважды, вы можете перевести шаблонный файл с помощью

 sed 's/.*/"&" "&"/' patternfile # Change the / into #, thats easier for the final command sed 's#.*#"&" "&"#' patternfile 

Когда вы даете команду sed прочитать командный файл, вам нужно запустить каждую строку с помощью sed . Командный файл будет выглядеть так:

 sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#;s#&#\\&#g' patternfile 

Вы можете сохранить это файл и использовать файл, но с заменой процесса вы можете делать такие вещи, как

 cat <(echo "Now this line from echo is handled as a file") 

Ницца. Давайте проверим решение

 sed -f <(sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#' patternfile) inputfile 

Почти готово! Странно, что только первая выходная линия. Что происходит?
Первый шаблон имеет & , и это имеет особое значение.
Мы можем исправить нашу команду, добавив обратную косую черту в шаблоне:

 sed -f <(sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#;s#&#\\&#g' patternfile) inputfile