Комментируйте все строки от последней закомментированной строки до строки с 'foo'

12

Рассмотрим текстовый файл users.txt:

#alice
#bob
charlie
dotan
eric

Мне нужно прокомментировать все от (эксклюзивно) последней закомментированной строки до (включительно) dotan. Это результат:

#alice
#bob
#charlie
#dotan
eric

Есть хороший sedприятель, чтобы сделать это? Я буду счастлив с любым инструментом, не только sed, действительно.

В настоящее время я получаю номер строки последней закомментированной строки следующим образом:

$ cat -n users.txt | grep '#' | tail -n1
  2 #bob

Затем я добавляю один и комментирую sed:

$ sed -i'' '3,/dotan/ s/^/#/' users.txt

Я знаю, что мог бы быть умным и сложить все это вместе с некоторыми bcв уродливую строчку. Конечно, должен быть более чистый путь?

dotancohen
источник

Ответы:

5

Как насчет

perl -pe '$n=1 if s/^dotan/#$&/; s/^[^#]/#$&/ unless $n==1;' file

или та же идея в awk:

awk '(/^dotan/){a=1; sub(/^/,"#",$1)} (a!=1 && $1!~/^#/){sub(/^/,"#",$1);}1; ' file
Тердон
источник
7

Если существующие закомментированные строки образуют единый непрерывный блок, то вместо этого вы можете сопоставить с первой закомментированной строки, закомментировав только те строки вплоть до вашего конечного шаблона, которые еще не закомментированы

sed '/^#/,/dotan/ s/^[^#]/#&/' file

Если существующие комментарии не являются смежными, то из-за жадной природы совпадения диапазона sed я думаю, что вам нужно сделать что-то вроде

tac file | sed '/dotan/,/^#/ s/^[^#]/#&/' | tac

то есть совпадение вверх от конечного шаблона до «первого» комментария - очевидно, это не очень удобно, если вы хотите получить решение на месте.

steeldriver
источник
4

Вы можете обрабатывать оба случая (закомментированные строки в одном смежном блоке или перемежающиеся между некомментированными строками) одним sedвызовом:

sed '1,/PATTERN/{/^#/{x;1d;b};//!{H;/PATTERN/!{1h;d};//{x;s/\n/&#/g}}}' infile

Это обрабатывает только линии в 1,/PATTERN/диапазоне. Это xменяет пространство удержания w. шаблонное пространство каждый раз, когда строка закомментирована (таким образом, в буфере удержания никогда не бывает более одной закомментированной строки), и добавляет каждую строку, которая не закомментирована, к Hстарому пространству (когда на 1-ой строке, 1dи, соответственно 1h, также необходима для удаления начальной пустая строка в буфере удержания).
Когда она достигает соответствия линии PATTERN, он также присоединяет его к Hстарому буфера, е xизменяет буфера , а затем заменяет каждый \newline символ в шаблоне пространстве с \newline и а #(то есть, все строки шаблона теперь будут начинаться с #, включение первой строки в качестве первой строки в области удержания всегда является строкой с комментариями).
С образцом infile:

alice
#bob
bill
#charlie
ding
dong
dotan
jimmy
#garry

Бег:

sed '1,/dotan/{                   # if line is in this range    -start c1
/^#/{                             # if line is commented        -start c2
x                                 # exchage hold space w. pattern space
1d                                # if 1st line, delete pattern space
b                                 # branch to end of script
}                                 #                             -end c2
//!{                              # if line is not commented    -start c3
H                                 # append to hold space
/dotan/!{                         # if line doesn't match dotan -start c4
1h                                # if 1st line, overwrite hold space
d                                 # delete pattern space
}                                 #                             -end c4
//{                               # if line matches dotan       -start c5
x                                 # exchage hold space w. pattern space
s/\n/&#/g                         # add # after each newline character
}                                 #                             -end c5
}                                 #                             -end c3
}' infile                         #                             -end c1

выходы:

alice
#bob
bill
#charlie
#ding
#dong
#dotan
jimmy
#garry

поэтому он комментирует только строки от (и исключая) #charlieдо (и включая) dotanи оставляя другие строки без изменений.
Конечно, это предполагает, что перед совпадением всегда есть хотя бы одна закомментированная строка PATTERN. Если это не так, вы можете добавить дополнительную проверку перед заменой:/^#/{s/\n/&#/g}

don_crissti
источник
Спасибо, у меня будет немало, чтобы узнать из этого ответа!
dotancohen
Подожди, я, должно быть, облажался. Это не о последней серии закомментированных строк? Нет, я понимаю, это так. Последняя серия + дотан. Довольно чертовски умный.
mikeserv
1
Вы всегда найдете лучшие вопросы. Проклятый дотан бросил меня на некоторое время - возможно, все еще делает, я еще не проверял это. спасибо дон
mikeserv
2

Вот еще один sed:

sed  -e:n -e'/\n#.*\ndotan/!{$!{N;/^#/bn'      \
-eb  -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \
-et  -e\} -eP\;D <in >out

Это делает, как вы просите. Он просто работает со стеком - строит его при необходимости и столько времени, сколько необходимо между вхождениями закомментированных строк, и сбрасывает старый буфер в пользу новой закомментированной строки далее на входе, когда находит ее. Картина...

введите описание изображения здесь

Извините, я не знаю, почему я это сделал. Но это пришло в голову.

В любом случае, sedраспределяет свои буферы между каждой последней закомментированной строкой в ​​любом ряду, никогда не сохраняя в своем буфере больше, чем необходимо для точного отслеживания последнего закомментированного вхождения, и, если в любое время он встречает последнюю строку при этом, он попытается последний gоператор выполнения lobal и ветвь создают tвесь буфер для печати, иначе он будет Pнабирать все те строки, которые он освобождает из своего буфера, как только это произойдет.

Я думаю, это то, что принесло аккордеоны в голову ...

printf %s\\n   \#alice \#bob charlie dotan eric \
               \#alice \#bob charlie dotan eric \
               \#alice \#bob charlie dotan eric |
sed  -e:n -e'l;/\n#.*\ndotan/!{$!{N;/^#/bn'     \
-eb  -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g'  \
-et  -e\} -eP\;D

#alice
#alice\n#bob$
#alice\n#bob\ncharlie$
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob
#charlie
#dotan
eric

Есть только одно различие между этой командой и той, что выше, и это команда look вверху. Когда мы lООК в sed«s шаблона , как это работает , мы можем получить лучшее представление о том, что происходит за кулисами , и лучшее понимание того , как направить свои усилия.

В этом случае мы можем наблюдать за sedвводом в стек до тех пор, пока не обнаружим второе вхождение \n#.*\ndotanна входе, и когда он начнет печатать предыдущую строку за раз. Это круто. Я многому научился, работая над этим.

mikeserv
источник
Очень хорошо, спасибо! Последний абзац с пояснениями потрясающий, я тоже потрачу немало времени на изучение этого поста. Хороший стек!
dotancohen
1
@dotancohen - это был действительно хороший вопрос. Посмотрите на редактирование, чтобы увидеть стек .
mikeserv
2
Я замечаю в истории редактирования запись Handle many dotans. Я уверен, что это худший кошмар моей жены.
dotancohen
1
@dotancohen - да, это было сложно. Вещи, как #\ndotan\ndotanтрудно для этих вещей. Я имею в виду, когда я говорю это хороший вопрос. Я думаю , что я получил его почти идеально, но одна проблема , которую вы могли бы столкнуться, если ваш комментарий блоки отделены друг от друга на 1000 строк - это будет замедлять его. Вы можете вставить что-то вроде s/\n/&/150;tin перед первым /\n#, чтобы разбить буфер, например, если он занимает 150 строк. И вообще, может быть, это именно то, чего она ждала все это время !
mikeserv