2016-12-16 5 views
3

Мне сложно понять, как достичь того, что я хочу использовать awk, и после поиска довольно долгое время я не мог найти решение, которое я ищу.Использование awk для форматирования текста

У меня есть входной текст, который выглядит следующим образом:

Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(
Element 4 
) 
Another line 
(
Element 1, span 1 to 
Element 5, span 4 
) 
Another Line 

Я хочу, чтобы правильно форматировать странные линии между «(» и «)». Ожидаемый результат выглядит следующим образом:

Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(Element 4) 
Another line 
(Element 1, span 1 to Element 5, span 4) 
Another Line 

Глядя на переполнение стека я нашел это:
How to select lines between two marker patterns which may occur multiple times with awk/sed

Так что я использую сейчас echo $text | awk '/ \(/{flag=1;next}/\)/{flag=0}flag'

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

(Element 4) 
(Element 1, span 1 to Element 5, span 4) 

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

Бонусное очко, если вы научите меня, как удалить syntaxic окраски на моем кодовых блоках вопроса :)

Спасибо миллиарда раз

Edit: Итак, я принял @ решение EdMorton как он дал что-то используя awk (ну, GNU awk). Тем не менее, я в настоящее время использую заклинания sed voooo @ aaron с большим успехом и, вероятно, продолжу это делать до тех пор, пока не удалю что-нибудь новое по этой конкретной утилите.

Я настоятельно рекомендую прочитать объяснения Эдмонтона, последний абзац сделал мой день. Если у кого-то есть хорошие ресурсы, связанные с awk/sed, они могут делиться, не стесняйтесь делать это в комментариях.

+2

Вы можете использовать '< ! - language: lang-none -> ', чтобы не выделять блок кода. См. [Подсветка синтаксиса] (http://stackoverflow.com/editing-help#syntax-highlighting). – e0k

+0

Итак, вы хотите напечатать то, что находится в круглых скобках '()', но также и то, что снаружи? Единственная модификация для удаления разрывов строк между '()'? – e0k

+0

@ e0K да, точно, и большое спасибо за синтаксический фокус. Должен признаться, что мне было слишком лениво искать это после столь многих поисковых запросов, касающихся моей проблемы с awk :) – daformat

ответ

3

sed для простых подстановок на отдельных линиях, то есть все.Если вы попытаетесь сделать что-то еще с этим, тогда вы используете конструкции, которые стали устаревшими в середине 1970-х годов, когда awk был изобретен, почти наверняка не переносимы и неэффективны, всегда просто куча неразборчивых тайных рун и используются сегодня просто для умственных упражнений.

Следующая использует GNU AWK для мульти-гольцов RS, RT и \s стенографии для [[:space:]] и работает, просто выделяя (...) строки, а затем делать все, что вы хотите с ними:

$ cat tst.awk 
BEGIN { 
    RS="[(][^)]+[)]"    # a regexp for the string you want to isolate in RT 
    ORS=""      # disable appending of newlines so we print as-is 
} 
{ 
    gsub(/\n[[:blank:]]+$/,"\n") # remove any blanks before RT at the start of each line 

    sub(/\(\s+/,"(",RT)   # remove spaces after (in RT 
    sub(/\s+\)/,")",RT)   # remove spaces before) in RT 
    gsub(/\s+/," ",RT)   # compress each chain of spaces to one blank char in RT 

    print $0 RT     # print the result 
} 

$ awk -f tst.awk file 
Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(Element 4) 
Another line 
(Element 1, span 1 to Element 5, span 4) 
Another Line 

Если вы учитывая использование решения sed для этого, также подумайте над тем, как вы могли бы улучшить его, если/когда у вас возникнут малейшие изменения требований. Любое изменение вышеуказанного кода awk было бы тривиальным и очевидным, в то время как изменение эквивалентного кода sed потребовало бы сначала жертвовать козу под луной крови, а затем разбить вашу копию Rosetta Stone ...

+1

Я абсолютно обожаю последний абзац вашего ответа. И мой вопрос касался использования awk, поэтому я считаю, что это, вероятно, будет принятым ответом. – daformat

5

Вот как я бы сделал это с GNU sed:

s/^\s*(/(/;/^(/{:l N;/)/b e;b l;:e s/\n//g} 

Который, для тех, кто не говорит чепуху, означает:

  • удалить начальные пробелы из строки, которые начинаются с пробелами и открывающий кронштейн
  • проверить, начинается ли линия с открывающей скобы.Если это так, выполните следующие действия:
    • отметьте это место в качестве метки l, который обозначает начало цикла
    • добавить строку из ввода в область шаблона
    • тест, если у Вас теперь есть закрывающая скобка в шаблоне пространстве
    • , если да, то переход к метке e
    • (если нет) перейти к метке l
    • знака этого пятна в качестве метки e, которые обозначают ы конец кода
    • удалить символы новой строки из шаблона
  • (неявно распечатайте шаблон пространство, был ли он изменен или нет)

Это, вероятно, может быть уточнена, но это делает трюк:

$ echo """Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(
Element 4 
) 
Another line 
(
Element 1, span 1 to 
Element 5, span 4 
) 
Another Line """ | sed 's/^\s*(/(/;/^(/{:l N;/)/b e;b l;:e s/\n//g}' 

Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(Element 4) 
Another line 
(Element 1, span 1 to Element 5, span 4) 
Another Line 

Edit: если вы можете отключить расширение истории (set +H), эта sed команда симпатичнее: s/^\s*(/(/;/^(/{:l N;/)/!b l;s/\n//g}

+0

Ну, это было быстро, оно почти работает, однако я все еще получаю пробел в начале строк, начиная с круглых скобок с моего ввода. – daformat

+0

@MathieuJouhet Я отредактировал его, чтобы удалить ведущее пространство – Aaron

+0

Это абсолютно идеальный @Aaron, и вы тоже нашли время, чтобы объяснить тарабарщину, SUPER полезно :) Не возражаете, если я подожду немного, прежде чем принимать ваш ответ? Мне было бы интересно узнать, какие другие решения люди могут предложить до этого. – daformat

0

С awk

$ cat fmt.awk 
function rem_wsp(s) { # remove white spaces 
    gsub(/[\t ]/, "", s) 
    return s 
} 

function beg() {return rem_wsp($0)=="("} 
function end() {return rem_wsp($0)==")"} 
function dump_block() { 
    print "(" block ")" 
} 

beg() { 
    in_block = 1 
    next 
} 

end() { 
    dump_block() 
    in_block = block = "" 
    next 
} 

in_block { 
    if (length(block)>0) sep = " " 
    block = block sep $0 
    next 
} 

{ 
    print 
} 

END { 
    if (in_block) dump_block() 
} 

Использование:

$ awk -f fmt.awk fime.dat 
0

Это выполнимо в awk, и, возможно, есть дождевик способ, чем это. Он ищет линии между и включая те, которые содержат только пробелы, и либо открытую, либо закрывающую скобки, и обрабатывает их специально. Все остальное он просто печатает:

awk '/^ *\(*$/,/^ *\) *$/ { 
     sub(/^ */, ""); 
     sub(/ *$/, ""); 
     if ($1 ~ /[()]/) hold = hold $1; else hold = hold " " $0 
     if ($0 ~ /\)/) { 
      sub(/\(/, "(", hold) 
      sub(/ \)/, ")", hold) 
      print hold 
      hold = "" 
     } 
     next 
    } 
    { print }' data 

Переменная hold изначально пуст. Первая пара sub вызывает полосы ведущих и завершающих пробелов (копирование данных с вопроса, после span 1 to). if добавляет ( или ) в hold без пробела или линии до hold после пробела. Если имеется закрывающая скобка, удалите пробел после открытой круглой скобки и перед закрывающей скобкой напечатайте hold и сбросьте hold на пустой. Всегда пропустите оставшуюся часть скрипта с next. Остальная часть скрипта { print } - печатать безоговорочно, часто написано 1 минималистами.

Файл data является copy'n'paste из данных в вопросе.

Выход:

Some text (possibly containing text within parenthesis). 
Some other text 
Another line (with something here) with some text 
(Element 4) 
Another line 
(Element 1, span 1 to Element 5, span 4) 
Another Line 

«Другая линия» (с большой буквы) имеет конечные заготовки, так как данные в вопросе делает.