Sed, сопоставьте все линии между 2 узорами (включительно), а затем поместите второе совпадение вверху

#awk #sed

Вопрос:

Это то, что мне нужно сделать:

Ввод: (Python)

 ## This is a function,
## its purpose is... yada yada yada
def function_name(x):
    return x   1
 

Вывод: (Уценка)

 ## function_name
This is a function,
its purpose is... yada yada yada
 

До сих пор я получал:

 sed -n '/## /,/def/ { /## |def/ p }' TEST.py | cut -d' ' -f2- | sed 's/(.*)(.*)://'
 

Который производит:

 This is a function,
its purpose is... yada yada yada
function_name
 

Есть 2 условия:

1.- Линии между «##» и «def», которые не соответствуют ни одному из этих шаблонов, следует игнорировать.

Пример:

Ввод: (Python)

 ## This is a function,
## its purpose is... yada yada yada
# This is a normal comment, nothing to see here! (ignored)
def function_name(x):
    return x   1
 

Вывод: (Уценка)

 ## function_name
This is a function,
its purpose is... yada yada yada
 

2.- Второй шаблон должен быть обобщен, например, вместо использования «def», допустим, я хочу использовать «класс».

Ввод: (Python)

 ## This is a class,
## its purpose is... yada yada yada
# This is a normal comment, nothing to see here! (ignored)
class class_name:
    __init__(self, x):
        self.x = x
 

Вывод: (Уценка)

 ## class_name
This is a class,
its purpose is... yada yada yada
 

Ответ №1:

Использование любого awk в любой оболочке в каждом окне Unix (используйте a-zA-Z0-9 вместо [:alnum:] , если ваш awk не поддерживает классы символов POSIX):

 $ cat tst.awk
sub(/^## */,"") {
    cmts = cmts $0 ORS
    next
}

sub(/^[[:alnum:]_]  */,"") {
    sub(/[^[:alnum:]_].*/,"")
    print "##", $0
    print cmts
    cmts = ""
}
 
 $ awk -f tst.awk file
## function_name
This is a function,
its purpose is... yada yada yada

## function_name
This is a function,
its purpose is... yada yada yada

## class_name
This is a class,
its purpose is... yada yada yada
 

Вышесказанное было выполнено в этом входном файле:

 $ cat file
## This is a function,
## its purpose is... yada yada yada
def function_name(x):
    return x   1

## This is a function,
## its purpose is... yada yada yada
# This is a normal comment, nothing to see here! (ignored)
def function_name(x):
    return x   1

## This is a class,
## its purpose is... yada yada yada
# This is a normal comment, nothing to see here! (ignored)
class class_name:
    __init__(self, x):
        self.x = x
 

Ответ №2:

С помощью awk :

 $ awk '/^## / { comment = comment substr($0, 4) "n" }
       /^def / { printf "## %sn%s", substr($0, 5, index($0, "(") - 5), comment
                 comment = "" }' TEST.py  
## function_name
This is a function,
its purpose is... yada yada yada
 

Добавляет все строки, начинающиеся с ## , в переменную, и когда он видит строку, начинающуюся с def , печатает имя функции и переменную с более ранними строками, а затем пропускает эту переменную, чтобы начать снова.


sed Версия GNU, использующая пробел для хранения строк комментариев:

 $ sed -n -e '/^## / { s/^## //; H }' -e '/^def / { s/^def ([^(]*).*/## 1/; G; s/nn/n/; p; z; x }' TEST.py
## function_name
This is a function,
its purpose is... yada yada yada
 

Комментарии:

1. Это однострочный, хотя, если вам нужна одна фактическая строка, вам не нужны разрывы строк в приведенном аргументе. Просто так выглядит уродливее, и вам понадобится точка с запятой между двумя утверждениями во втором блоке.

2. Да, я понял это после того, как реализовал его

3. Хотя, есть 2 небольшие проблемы, которые, как я думал, можно было бы обобщить, но я думаю, что мне все равно придется их решить

4. Хорошо, я отредактировал ответ, срри за то, что раньше не был откровенным

5. Впечатляющее использование пространства для хранения sed (H). Я должен признать, что не понял код, начинающийся с G; — но я рассмотрю документацию GNU sed для получения некоторой помощи там. Я определенно кое-чему научился.

Ответ №3:

 awk '
/##/ { 
  gsub( "## *", "" )  
  previous[   lines ] = $0 
}
/def/ { 
  gsub( "def ", "")  
  gsub( "[(].*","" ) 
  print "## " $0  
  for (x = 1 ; x <= lines ; x   ) 
    print previous[x] 
  lines = 0 
}
' TEST.py
 

Похоже, Шон опередил меня с ответом. Ответы похожи, они отличаются в основном тем, как накапливаются строки. Шон накапливает предыдущие строки в одной переменной (комментарий), в то время как я накапливаю их в массиве.

Я думаю, что у Шона могут быть дополнительные предположения в коде, например, у каждого ## есть один пробел после него или имена функций начинаются с 5-го символа в строке, и что ## и def оба появляются в начале строки.

Я думаю, что мне тоже определенно следует добавить несколько якорей, так как в /def/ может быть много непреднамеренных совпадений, так что /^def/ может быть намного лучше.

Интересно то, что мы оба игнорировали использование sed, так как лично я понятия не имею, как работать с переменными в sed. Мне интересно посмотреть, будут ли доступны другие решения от сообщества, которые являются чистыми sed.

Комментарии:

1. Я добавил версию sed к своему ответу.

2. Да, вы должны использовать якоря. Кроме того, все ваши gsub() s должны быть sub() s, так как вы всегда хотите что-то сделать только с первым совпадением, и если вы сделаете это со всеми совпадениями в строке, то вы можете испортить комментарии, например ## I prefer 4 hashes, #### is clearer , или имена функций, например def mydef () .

Ответ №4:

Это может сработать для вас (GNU sed):

 sed -En '/^##/{h;:a;n;/^##/{H;ba}
        s/^(def|class)( w*).*/##2/;Ta;G;s/^## ?//2mgp}' file
 

Если текущая строка не начинается ## , ничего не делайте.

Замените место удержания текущей строкой.

Достань следующую строчку.

Если строка начинается ## , добавьте ее в область удержания и повторите выше.

Если следующая строка не начинается def или class не повторяется, как указано выше.

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

Комментарии:

1. Это s/## ?//2mgp прекрасная вещь.