Как я могу редактировать диапазон текста между 2 символами? awk, sed, regex

Используя символ "*" (это не обязательно должен быть любой специальный символ, чтобы указать), как я могу отредактировать текст из этого:

*berry
straw
rasp
blue
boysen
*
blahblah
blahblah
blahblah
*berry
straw
blue
*
blah
*table
vege
pingpong
*

К этому:

strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Каждый символ после первой подходящей звездочки будет помещен в каждую строку, пока не будет найдено 2-е совпадение звездочек.

Есть какие-нибудь указания о том, как я могу это сделать? (sed или awk предпочтительнее, но если вы можете придумать другой способ, пожалуйста, напишите мне свой код!)

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

5 ответов

Решение

Это awk кода может быть достаточно:

awk -F'*' 'NF == 2 {label = $2; next} {$0 = $0 label} 1'

Чтобы сломать это:

  • использование * в качестве разделителя полей. Таким образом, мы можем просто проверить количество полей (NF) определить, достигнут ли начало или конец блока.
  • Когда есть два поля, мы сохраняем второе поле в label и перейти к следующей строке.
  • С тех пор мы добавляем, что label к текущей строке, а затем распечатайте. Если метка пуста, мы находимся вне блока, и никакого эффекта нет. Если нет, мы получаем требуемый результат.

В sedВы можете скопировать "специальную" строку в пространство для хранения перед удалением

sed -e '/^\*/{h;d;}'

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

    -e '{G;s/\n\*//;}'

Проверяя это с вашими данными,

$ sed -e '/^\*/{h;d;}' -e '{G;s/\n\*//;}' file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Примечание: это не останавливается, когда он встречает вторую звездочку; он делает то же самое, но добавляет * ничего не следует - пока не совпадет со следующим *sometext,

Вот способ Perl:

$ perl -lne '/^\*(.*)/ || print "$_$1"' file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

объяснение

-n заставит Perl прочитать каждую строку входного файла, сохранив ее в специальной переменной $_, -l приведет к тому, что я) лишить завершающие символы новой строки (\n) из каждой строки и II) добавить новую строку для каждого вызова print, -e скрипт, который применяется к каждой строке

  • /^\*(.*)/: сопоставить строки, начинающиеся со звездочки, и сохранить все после звездочки как $1 (это то, что делают скобки).

  • || print "$_$1"': || логично OR, Следовательно print будет выполняться только в том случае, если текущая строка не начинается со звездочки. Если это так, мы печатаем текущую строку ($_) вместе с тем, что в настоящее время сохраняется как $1 (шаблон, следующий за звездочкой).


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

$ while read line; do 
    [[ $line =~ ^\* ]] && pat="${line#\*}" || printf "%s%s\n" "$line" "$pat"; 
  done < file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

объяснение

  • while read line; do ... ; done < file: это классика while цикл, который будет читать каждую строку входного файла file и сохранить его как $line,
  • [[ $line =~ ^\* ]] && pat="${line#\*}": если строка начинается с *, удали все после этого (вот что ${line#\*} делает, для более подробной информации, см. здесь) и сохранить его как $pat, *|| printf "%s%s\n" "$line" "$pat";: если предыдущая команда завершилась неудачно (строка не начинается со звездочки), выведите строку и текущее значение $pat,

Через мой любимый питон...

with open('/path/to/the/file') as f:
    counter = False
    for line in f:
        if line.startswith('*') and not counter:
            m = line.strip().lstrip('*')
            counter = True
        elif line.startswith('*') and counter:
            counter = False    
        elif counter:
            if not line.startswith('*'):
                print(line.strip() + m)
        else:
            print(line.strip())  

Пришел сюда поздно. Вот еще один python подход:

#!/usr/bin/env python2
with open('/path/to/file.txt') as f:
    for lines in f.read().split('*'):
        entries = lines.rstrip().split('\n')
        for i in range(1, len(entries)):
            print entries[i] + entries[0]
Другие вопросы по тегам