#bash #sed #grep
Вопрос:
У меня есть очень большой файл, в котором мне нужно найти 40 шаблонов. Если шаблон совпал в файле, то нужно прокомментировать до 2 строк и после 2 строк. Шаблоны будут такими, как показано ниже:
1.create_rev -name "2x_8_PLL"
2.create_generated_rev -name "76_L"
3.create_rev -name "PCS_T0"
4.create_generated_rev -name "x544_P"
Если мне нужно найти один шаблон, я могу выполнить команду gvim ниже, чтобы выполнить задачу.
:g/create_rev -name "2x_8_PLL"/-2, 2s/^/#
Но шаблоны поиска больше в количестве 40 с лишним. Как искать/grep для 40 шаблонов, чтобы мой ожидаемый результат, как показано ниже:
#pp
#oo
create_rev -name "2x_8_PLL"
#aa
#bb
hh
#ii
#jj
create_generated_rev -name "76_L"
#cc
#dd
create_rev -name "PCS_T0"
#ee
#ff
gg
Комментарии:
1. Вы можете использовать трубу (
|
) для разделения каждого шаблона::g/create_rev -name "2x_8_PLL"|create_generated_rev -name "76_L"/-2, 2s/^/#
. Конечно, в опубликованном примере вы также можете просто сделать:g/create/-2, 2s/^/#
это, возможно, вы сможете придумать один или несколько шаблонов, которые в совокупности захватят 40, которые вы хотите (может быть, что-то вроде:g/create_.* -name ".*"/-2, 2s/^/#
)2. @mattb, Да, я могу использовать трубопровод и сделать то же самое в gvim .. но мне нужно сделать это либо в sed, либо в grep, либо в bash с помощью массива. Я не могу использовать .* создайте шаблон в моем случае.
3.
awk
ниже приведено то, что я бы использовал. Если вам действительно нужноsed
искатьsed -f script.sed input > output
типовые решения. Некоторые идеи см. в руководстве sed . Удачи.
Ответ №1:
Это может сработать для вас (GNU grep и sed):
grep -A2 -B2 -nFf targets file |sed -En 's/^([0-9] )-.*/1s@^@#@/p' |sed -f - file
Используйте grep для вывода строк в файле, соответствующих строкам в целевых объектах. Совпадения будут пронумерованы строками и содержать две строки до и после совпадений.
Строки, выводимые командой grep, передаются в sed и используются в качестве адресов для сценария sed, который вставляет a #
в начале каждого соответствующего адреса.
Сценарий sed, созданный в результате первого вызова sed (с помощью опции -f
командной строки и -
с использованием stdin из канала), используется во втором вызове sed, который редактирует исходный файл.
Другое решение, использующее только sed:
sed -E 's/.*/\#\n.*\namp;\n.*\n#bb/' targets |
sed -Ee ':a;N;s/n/amp;/4;Ta' -f - -e 'bc;:b;s/^([^#])/#1/mg;s/^#//m3;:c;P;D' file
Ответ №2:
Предполагая, что, когда вы говорите «шаблон», то, что вы действительно хотите,-это соответствие строк в полной строке, затем используйте любой awk в любой оболочке на каждом блоке Unix и обрабатывайте случаи перекрытия диапазонов, комментируя их как предположительно необходимые, а не дважды комментируя их, как это может произойти с другими решениями:
$ cat tst.awk
ARGIND==1 {
targets[$0]
next
}
ARGIND==2 {
if ($0 in targets) {
for (i=FNR-2; i<=FNR 2; i ) {
if (i != FNR) {
hits[i]
}
}
}
next
}
FNR in hits {
$0 = "#" $0
}
{ print }
$ awk -f tst.awk targets file file
#pp
#oo
create_rev -name "2x_8_PLL"
#aa
#bb
hh
#ii
#jj
create_generated_rev -name "76_L"
#cc
#dd
create_rev -name "PCS_T0"
#ee
#ff
gg
$ cat targets
create_rev -name "2x_8_PLL"
create_generated_rev -name "76_L"
create_rev -name "PCS_T0"
create_generated_rev -name "x544_P"
вышесказанное использует GNU awk для ARGIND. Если у вас нет GNU awk, перейдите ARGIND==1
на FILENAME==ARGV[1]
и ARGIND==2
на FILENAME==ARGV[2]
.
Комментарии:
1. Повторяется ли «очень большой файл» дважды?
2. @ceving да, это повторяется дважды с помощью простого, надежного, чрезвычайно быстрого кода, использующего очень мало памяти (для всех, кроме 40 строк, все, что произойдет на первой итерации,-это поиск хэша, и то же самое для не более 160 строк на второй итерации), в отличие от повторения один раз с некоторой комбинацией более сложного, хрупкого, более медленного и более интенсивного кода с памятью.
Ответ №3:
Если ed
доступно/приемлемо с некоторой помощью оболочки.
Сценарий myscript
#!/bin/sh
targets=$1
file=$2
{
ed -s "$targets" <<'EOF'
g|.|t.
-1s|^|g/|
s|$|/-2; 1s/^\(#\)\{0,1\}\(.*\)/#\2/\|
1s|.*|; 2; 1s/^\(#\)\{0,1\}\(.*\)/#\2/|
$a
,p
Q
.
,p
Q
EOF
} | ed -s "$file"
./myscript targets file
- Удалите первый
,p
, чтобы отключить вывод вstdout
- Измените первое
Q
значение наw
, если требуется редактирование на месте. - Проблемы с памятью
ed
могут возникать в зависимости от того, насколько она великаbig file
.