Como exibo todos os caracteres entre duas strings específicas?

2

Eu quero exibir todos os caracteres em um arquivo entre as strings "xxx" e "yyy" (as aspas não fazem parte dos delimitadores). Como eu posso fazer isso ? Por exemplo, se eu tiver inserido "Hello world xxx, este é um arquivo yyy", a saída deve ser "this is a file"

    
por Out Of Bounds 31.03.2016 / 22:53

5 respostas

1

Você pode usar o sinalizador de correspondência de padrões em sed da seguinte forma:

echo "Hello world xxx this is a file yyy" | sed 's/.*xxx \(.*\)yyy//'

Portanto, .*xxx corresponderá do início até xxx . Isso é melhor mostrado usando grep :

é um 'Padrão de recordes' que lembra tudo o que está dentro de \(.*\) , de xxx até yyy , mas não yyy .

Finalmente, a sequência lembrada é impressa.

    
por 31.03.2016 / 23:14
2

A questão só é interessante se os delimitadores não estiverem necessariamente na mesma linha. Isso pode ser feito de várias maneiras (mesmo com sed ), mas awk é mais flexível:

    #!/bin/sh
    awk '
    BEGIN { found = 0; }
    /xxx/ {
        if (!found) {
            found = 1;
            $0 = substr($0, index($0, "xxx") + 3);
        }
    }
    /yyy/ {
        if (found) {
            found = 2;
            $0 = substr($0, 0, index($0, "yyy") - 1);
        }
    }   
        { if (found) {
            print;
            if (found == 2)
                found = 0;
        }
    }
    '

Isso é testado levemente para os casos em que no máximo uma substring está em uma linha, usando esses dados:

    this is xxx yy
    first
    second yyy

    xxx.x
    yyy

    xxx#yyy

e esta saída (o script é "foo", os dados são "foo.in"):

    $ cat foo.in|./foo
     yy
    first
    second 
    .x

    #

A maneira como funciona, é que os dados de entrada estão em $0 , e o awk combina os padrões xxx e yyy em sequência, permitindo que mais de uma coisa mude $0 até chegar ao último passo, onde é impresso.

A propósito, este exemplo não funcionaria para

xxxxHelloyyyxxxWorldyyy

pois verifica apenas a primeira correspondência. O script Perl dará resultados diferentes, já que ele usa uma correspondência gulosa em vez do índice / substr que usei no exemplo do awk. Perl, claro, pode fazer o mesmo - com um script.

O awk (como o Perl) é de formato livre, então pode-se expressar o comando como algo parecido com

awk 'BEGIN{found=0;}/xxx/{if(!found){found=1;$0=substr($0,index($0, "xxx")+3);}}/yyy/{if(found){found=2;$0=substr($0,0,index($0,"yyy")-1);}}{ if(found){print;if(found==2)found=0;}}'

mas isso raramente é feito, exceto por exemplo. Da mesma forma, sed scripts (orientados a linhas), podem ser combinados em uma única linha com algumas restrições. Novamente, scripts complexos em sed raramente são tratados dessa maneira. Em vez disso, eles são tratados como programas reais (consulte exemplo ).

Leitura adicional:

por 31.03.2016 / 23:40
2

Isso deve fazer o que você está tentando fazer:

sed -e 's/xxx\(.*\)yyy//'

Isso pressupõe que as duas cadeias de caracteres do delimitador estão na mesma linha

    
por 31.03.2016 / 23:05
2

Aqui está uma solução com python:

import sys
import re
F=open(sys.argv[1])
text=F.read()
reg=re.compile("xxx((?:.|\n)*)yyy")
for match in reg.finditer(text):
    print match.groups()[0]

Salve este script como um arquivo "post.py" e lance-o com:

python post.py your_file_to_search_in.txt

O script compila uma expressão regular e imprime todas as ocorrências encontradas no texto do arquivo.

(?:. | \ n) é um grupo sem captura correspondente a qualquer caractere, incluindo nova linha

Editar: solução aprimorada graças às dicas do 1_CR:

import sys
import re
F=open(sys.argv[1])
text=F.read()
reg=re.compile(r'xxx(.*)yyy',re.DOTALL)
for match in reg.finditer(text):
    print match.groups()[0]
    
por 01.04.2016 / 00:46
1

Uma solução que também funciona quando xxx e yyy não estão na mesma linha: cat /tmp/xxx-to-yyy| perl -ne '(/xxx/../yyy/) && print' | perl -pe 's/.*(xxx.*)/$1/' | perl -pe 's/(.*yyy).*/$1/'

Não exatamente bonito ...

A opção -e para perl é apenas para fornecer o script na linha de comando. O -n e -p faz um loop sobre as linhas de entrada, com -p elas são impressas após o script, com -n elas não são. Então, basicamente, isso apenas envia o arquivo através de três loops perl.

.. é um operador de intervalo, que retorna falso até que a condição à esquerda retorne verdadeiro e falso após a condição correta retornar verdadeiro, portanto o primeiro loop reduz o arquivo para as linhas entre as duas cadeias (ambas incluídas. Os dois últimos comandos perl removem o texto antes de xxx e depois de yyy .

    
por 31.03.2016 / 23:20