#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
прекрасная вещь.