Como posso editar um intervalo de texto entre 2 símbolos? awk, sed, regex

6

Usando o símbolo "*", (não precisa ser esse, qualquer caractere especial serve para indicar), como posso editar o texto a partir disso:

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

Para isso:

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

Cada caracter após o primeiro asterisco correspondente será colocado em todas as linhas até que o segundo asterisco seja encontrado.

Alguma pista sobre como posso fazer isso? (sed ou awk seria o preferido, mas se você puder pensar em outra maneira, por favor, me mande seu código!)

Eu sei como remover todas as linhas contendo um asterisco, é apenas a parte de colocação de caracteres que eu não consigo imaginar

    
por TuxForLife 25.04.2015 / 03:49

5 respostas

12

Esse código awk pode ser suficiente:

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

Para dividir:

  • Use * como o separador de campo. Dessa forma, podemos simplesmente examinar o número de campos ( NF ) para determinar se o início ou o fim de um bloco foi atingido.
  • Quando há dois campos, salvamos o segundo campo em label e continuamos na próxima linha.
  • A partir de então, adicionamos label à linha atual e, em seguida, imprimimos. Se a etiqueta estiver vazia, estamos fora de um bloco e não há efeito. Caso contrário, obtemos a saída necessária.
por muru 25.04.2015 / 04:07
8

Em sed , você pode copiar a linha "especial" para o espaço de espera antes de excluí-la

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

e, em seguida, anexe o espaço de espera a cada espaço de padrão seguinte, substituindo a nova linha e o caractere de marcador resultantes

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

Testando com seus dados,

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

Nota: isto não pára quando encontra o segundo asterisco; ele faz exatamente o mesmo, mas está anexando * seguido de nada - até que corresponda ao próximo *sometext .

    
por steeldriver 25.04.2015 / 04:09
7

Aqui está um jeito Perl:

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

Explicação

O -n fará o Perl ler cada linha do arquivo de entrada, salvando-o na variável especial $_ , o -l fará com que ele i) tire novas linhas ( \n ) de cada linha e ii) adicionar uma nova linha a cada chamada de print . O -e é o script aplicado a cada linha.

  • /^\*(.*)/ : corresponde linhas que começam com um asterisco e salvam tudo depois do asterisco como $1 (é o que os parênteses fazem).

  • || print "$_$1"' : o || é um OR lógico. Portanto, o print será executado apenas se a linha atual não iniciar com um asterisco. Nesse caso, imprimimos a linha atual ( $_ ) junto com o que estiver salvo atualmente como $1 (o padrão após o asterisco).

Como de costume, há muitas maneiras de fazer isso. Um bobo e ineficiente, mas que destaca os recursos de manipulação de strings do shell, é:

$ 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

Explicação

  • while read line; do ... ; done < file : este é um loop while clássico que lerá cada linha do arquivo de entrada file e salvará como $line .
  • [[ $line =~ ^\* ]] && pat="${line#\*}" : se a linha começar com * , remova tudo depois disso (é isso que o ${line#\*} faz, para mais detalhes, veja aqui ) e salve-o como $pat . * || printf "%s%s\n" "$line" "$pat"; : se o comando anterior falhou (então, a linha não inicia com um asterisco), imprima a linha e o valor atual de $pat .
por terdon 25.04.2015 / 15:14
3

Através do meu Python favorito ...

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())  
    
por Avinash Raj 25.04.2015 / 17:01
3

Cheguei atrasado. Aqui está outra abordagem 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]
    
por heemayl 25.04.2015 / 23:09

Tags