Добавить что-то в каждый список в файле

У меня есть файл, lists.txt, который выглядит так:

// stuff at beginning of file var list1 = new Array(); i = 0; list1[i++] = 'a'; list1[i++] = 'b'; ... list1[i++] = 'z'; var list2 = new Array(); i = 0; list2[i++] = 'a'; list2[i++] = 'b'; ... list2[i++] = 'z'; // other stuff at end of file 

Мне нужно добавить к каждому из этих списков (их более двух), и в итоге получается что-то вроде этого:

 var list1 = new Array(); i = 0; list1[i++] = 'a'; list1[i++] = 'b'; ... list1[i++] = 'z'; list1[i++] = 'something new'; var list2 = new Array(); i = 0; list2[i++] = 'a'; list2[i++] = 'b'; ... list2[i++] = 'z'; list2[i++] = 'another thing'; // other stuff at end of file 

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

 list1_last=$(grep "list1\[i++\]" lists.txt | tail -1) list2_last=$(grep "list2\[i++\]" lists.txt | tail -1) 

Я знаю, как получить все между началом первого списка и началом второго списка (включительно):

 list1=$(sed -n '/var list1/,/var list2/p' lists.txt) 

Я знаю, что могу получить list1 без первой строки списка2 с помощью этого однострочного Perl или этого сумасшедшего сценария sed .

Но я с трудом собираю все штуки. Как мне это сделать?

редактировать

Дополнительные значения, которые я хочу добавить, находятся в другом файле, дополнительном-value.txt, который, например, содержит:

 list1[i++] = 'something new'; list2[i++] = 'another thing'; 

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

Изменить 2

Фактический файл выглядит примерно так:

 // comment // comment // ... var foo = "bar"; // comment // comment // ... var i= 0; // comment // comment // ... var GoodDomains = new Array(); i=0; GoodDomains[i++] = "anything.com"; // comment GoodDomains[i++] = "something.com"; // comment ... GoodDomains[i++] = "lastthing.com"; // comment // THIS IS WHERE I WANT TO INSERT SOMETHING // comment // comment // ... var BadDomains = new Array(); i=0; BadDomains[i++] = "anything.com"; // comment BadDomains[i++] = "something.com"; // comment ... BadDomains[i++] = "lastthing.com"; // comment // THIS IS WHERE I WANT TO INSERT SOMETHING // more lists, including GoodHosts, GoodURLs, etc. // comment // comment // ... for (i in GoodDomains) { ... } // loop through BadDomains, GoodHosts, GoodURLs, etc. // comment // comment // ... function IsNumIpAddr(host) { ... } 

Я изначально опубликовал упрощенную версию, потому что

  1. Я не уверен, будет ли фактический файл всегда следовать этому формату (комментарии вверху, объявления переменных, дополнительные комментарии, определения списков, функции и т. Д.).
  2. Я хотел бы найти общее решение проблемы (добавление материала в списки в середине файла)

Извините, если это вводит в заблуждение.

Поскольку вы пытаетесь использовать диапазоны sed , вот один из возможных способов сделать это. Строки в вашем additional-values.txt следуют одному и тому же шаблону:

 KEY[i++] = 'VALUE'; //etc 

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

 var KEY = new Array(); 

и пустую строку


поэтому вы можете обработать additional-values.txt и превратить его в сценарий sed который для каждой строки:

 /^var KEY = new Array();/,/^$/{ /^$/ i\ KEY[i++] = 'VALUE'; // etc } 

то есть в /^var KEY = new Array();/,/^$/ range, вставить строку KEY[i++] = 'VALUE'; // etc KEY[i++] = 'VALUE'; // etc перед пустой строкой. Затем вы используете скрипт для обработки lists.txt :

 sed 's/\\/&&/g' additional-values.txt | \ sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\ /^$/ i\\\ &\ }|' | sed -f - lists.txt 

Первый sed избегает любых обратных косых черт, второй sed обрабатывает additional-values.txt превращая его в скрипт, который используется третьим sed (через -f ) для обработки lists.txt .
например, образец additional-values.txt :

 GoodDomains[i++] = '^stuff/here/'; \ BadDomains[i++] = '%XYZ+=?\\<>'; GoodNetworks[i++] = '|*{};:\'; // Malware\\ BadDomains[i++] = '\$.|&$@"#"!||'; 

результат:

 sed 's/\\/&&/g' additional-values.txt | \ sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\ /^$/ i\\\ &\ }|' 

является

 /^var GoodDomains = new Array();/,/^$/{ /^$/ i\ GoodDomains[i++] = '^stuff/here/'; \\ } /^var BadDomains = new Array();/,/^$/{ /^$/ i\ BadDomains[i++] = '%XYZ+=?\\\\<>'; } /^var GoodNetworks = new Array();/,/^$/{ /^$/ i\ GoodNetworks[i++] = '|*{};:\\'; // Malware\\\\ } /^var BadDomains = new Array();/,/^$/{ /^$/ i\ BadDomains[i++] = '\\$.|&$@"#"!||'; } 

это затем передается в sed -f - lists.txt например, например, sample lists.txt :

 // Counter Variable to initalize the arrays. var i= 0; var GoodDomains = new Array(); i=0; GoodDomains[i++] = 'aba.com'; // Phish - 2010-02-05 var GoodNetworks = new Array(); i=0; GoodNetworks[i++] = '10.0.0.0, 255.0.0.0'; // NRIP // GoodNetworks[i++] = "63.140.35.160"; // DNSWCD 2o7 var BadDomains = new Array(); i=0; BadDomains[i++] = '.0catch.com'; // AdServer - 2009-06-16 //var BadDomains = new Array(); 

Бег:

 sed 's/\\/&&/g' additional-values.txt | \ sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\ /^$/ i\\\ &\ }|' | sed -f - lists.txt 

выходы:

 // Counter Variable to initalize the arrays. var i= 0; var GoodDomains = new Array(); i=0; GoodDomains[i++] = 'aba.com'; // Phish - 2010-02-05 GoodDomains[i++] = '^stuff/here/'; \ var GoodNetworks = new Array(); i=0; GoodNetworks[i++] = '10.0.0.0, 255.0.0.0'; // NRIP // GoodNetworks[i++] = "63.140.35.160"; // DNSWCD 2o7 GoodNetworks[i++] = '|*{};:\'; // Malware\\ var BadDomains = new Array(); i=0; BadDomains[i++] = '.0catch.com'; // AdServer - 2009-06-16 BadDomains[i++] = '%XYZ+=?\\<>'; BadDomains[i++] = '\$.|&$@"#"!||'; //var BadDomains = new Array(); 

Если вы предпочитаете gnu sed и замену процесса:

 sed -E 's|^([^[]*).*|/^var \1 = new Array();/,/^$/{/^$/ i\\\n&\ }|' <(sed 's/\\/&&/g' additional-values.txt) | sed -f - lists.txt 

Если вы отмените файл, вы можете добавить строку при первом просмотре:

 tac lists.txt | awk -v l1="list1" -v val1="something new" \ -v l2="list2" -v val2="another thing" ' index($0, l1"[i++]") && !found1 { printf "%s[i++] = \"%s\";\n", l1, val1 found1 = 1 } index($0, l2"[i++]") && !found2 { printf "%s[i++] = \"%s\";\n", l2, val2 found2 = 1 } {print} ' | tac > lists.txt.new 

Это немного не сухим, но все будет.

Я пропустил, что есть «extra-values.txt». Так лучше:

 tac lists.txt | awk ' NR == FNR {additional[$1] = $0; next} $1 in additional && !found[$1] {print additional[$1]; found[$1] = 1} {print} ' additional-values.txt - | tac > newfile 

Если списки в вашем входном файле разделены пустой строкой, вы можете использовать инструмент, который позволяет установить разделитель записей (что определяет «линию») на последовательные строки новой строки. Например, в Perl (предполагая, что ваши замены находятся в файле с additions ):

 perl -ne 'BEGIN{## Open the additions file open($fh,"additions"); while(<$fh>){ ## Get the name of the current list /list./; ## save this replacement in the %f hash $f{$&}=$_; } ## Set the record separator to consecutive newlines. $/="\n\n"; } ## Now that the BEGIN{} block is finished, process the ## input file. ## Does this line match "list."? if(/list./){ chomp; ## remove trailing newlines. ## Add the addition to this "line" $_.= "\n$f{$&}\n\n"; } ## print each input line print ' file в perl -ne 'BEGIN{## Open the additions file open($fh,"additions"); while(<$fh>){ ## Get the name of the current list /list./; ## save this replacement in the %f hash $f{$&}=$_; } ## Set the record separator to consecutive newlines. $/="\n\n"; } ## Now that the BEGIN{} block is finished, process the ## input file. ## Does this line match "list."? if(/list./){ chomp; ## remove trailing newlines. ## Add the addition to this "line" $_.= "\n$f{$&}\n\n"; } ## print each input line print ' file 

Вышеуказанное можно свести к:

 perl -ne 'BEGIN{open($fh,"additions"); while(<$fh>){/list./;$f{$&}=$_;}$/="\n\n";} if(/list./){chomp;$_.= "\n$f{$&}\n\n"; }; print ' file в perl -ne 'BEGIN{open($fh,"additions"); while(<$fh>){/list./;$f{$&}=$_;}$/="\n\n";} if(/list./){chomp;$_.= "\n$f{$&}\n\n"; }; print ' file 

Я наконец придумал что-то, что работает:

 # print from beginning of file to "var list1" (exclusive) sed "/var list1/,\$d" lists.txt > merged.txt # print from "var list1" to last member of array lastlist1=$(grep -n "list1\[i++\]" lists.txt | tail -1 | cut -f1 -d:) sed -n "/var list1/,$(echo $lastlist1)p" lists.txt >> merged.txt grep "^list1" additional-values.txt >> merged.txt # print from "var list2" to last member of array lastlist2=$(grep -n "list2\[i++\]" lists.txt | tail -1 | cut -f1 -d:) sed -n "/var list2/,$(echo $lastlist2)p" lists.txt >> merged.txt grep "^list2" additional-values.txt >> merged.txt # do this for list3, list4,... listn # print from last member of listn (exclusive) to end of file sed "1,$(echo $lastlistn)d" lists.txt >> merged.txt 

Это довольно утомительно и, вероятно, может быть улучшено, но, по крайней мере, я это понимаю.

Учитывая, что ваши списки разделены новыми строками, такими как

 var list1 = new Array(); i = 0; list1[i++] = 'a'; list1[i++] = 'b'; list1[i++] = 'z'; var list2 = new Array(); i = 0; list2[i++] = 'a'; list2[i++] = 'b'; list2[i++] = 'z';\n 

И если дополнительный-lists.txt выглядит так:

 list1[i++] = 'something new'; list2[i++] = 'another thing'; 

Затем этот сценарий bash / sed будет выдавать желаемый результат:

 #! /bin/bash a="lists.txt" b="additional-values.txt" while read line; do list=$(expr match "$line" '\(.*\[\)') list=${list::-1} sed -i "/$list\[i++\]/{:loop; n; /^$/{s/^$/$line\n/; b}; b loop;}" $a done < $b 

Он делает это, читая каждую строку add-values.txt и получая подстроку строки до [(мы предполагаем, что дополнительный-lists.txt имеет имя формата [i ++] …), например «list1 [", то он удаляет последний символ, чтобы получить имя списка. Затем он запускает сценарий sed, который соответствует имени списка (обратите внимание на использование двойных кавычек для использования переменной bash), а затем запускает цикл, который заканчивается при достижении пустой строки. Наконец, он заменяет пустую строку линией из дополнительных значений (и новой строки). Опция -i означает редактирование на месте.

Вывод:

  $ cat lists.txt var list1 = new Array(); i = 0; list1[i++] = 'a'; list1[i++] = 'b'; list1[i++] = 'z'; list1[i++] = 'something new'; var list2 = new Array(); i = 0; list2[i++] = 'a'; list2[i++] = 'b'; list2[i++] = 'z'; list2[i++] = 'another thing'; 

Использует диск Separator записи awk для: последней строки списка, плюс следующую пустую строку .

Как это работает :

Первоначально RS является \n (по умолчанию) – для входного файла, названного в 1-м аргументе: additional-values.txt
Сразу после чтения в первом файле awk меняет значение RS на значение в 2-м аргументе.
Второй файл lists.txt , названный в 3-м, arg, имеет RS определяемый 2-м аргументом

строка 1 : построить массив дополнительных значений
строка 2 : 1-е поле разделения – это текущий список – через RT (текст в соответствии с RS )
строка 3 : запись печати + разделитель записей (менее одного \n ) + дополнительные значения

  awk 'RS == "\n" { addval[$1] = addval[$1] $0 "\n"; next } { split(RT,crskey) print $0 gensub(/\n/,"","",RT) addval[ crskey[1] ] }' additional-values.txt \ RS='[^[\n]+[[]i[+][+][]] = [^;\n]+;\n\n' \ lists.txt