Como grep n-ésimo substring entre determinados delimitadores?

3

Digamos que tenhamos uma string e seu delimitador seja ? :

Leslie Cheung April 1 ? Elvis August 16 ? Leonard Nimoy February 27

Eu sei como grep a primeira subseqüência de caracteres entre os delimitadores:

echo $above_string | grep -oP "^[^?]*"
Leslie Cheung April 1

Como devo alterar o Regex para obter a segunda ou terceira substring?

    
por Abdul Al Hazred 31.03.2015 / 19:52

5 respostas

4
echo $above_string | grep -oP "^([^?]*\?){2}\K[^?]*"

Altere 2 para o valor n - 1 para obter a enésima string.

Isso pressupõe que você queira a enésima string nessa linha . Você tem n - 1 strings sem ? terminando com um literal '?' ( \? , já que é um caractere especial em perl regex). Então, com \K você declara que não está interessado no conteúdo anterior, extraindo apenas o seguinte texto até o próximo separador.

    
por 31.03.2015 / 23:46
10

Que tal usar o corte? Se você gostaria de imprimir o segundo padrão

echo "$above_string" | cut -f2 -d "?"

Segunda coluna em diante

echo "$above_string" | cut -f2- -d "?"

    
por 31.03.2015 / 19:56
4

Usando o Awk para imprimir o segundo e o terceiro registros separados por novas linhas:

awk -F"?" '{printf "%s\n%s\n", $2,$3}'
Elvis August 16 
Leonard Nimoy February 27

Se você quiser trocar o registro, pode defini-lo como uma variável:

awk -v record=2 -F"?" '{print $record}'
Elvis August 16 
    
por 31.03.2015 / 20:03
3

Com sed , você pode fazer:

sed '/\n/P;//d;s/[^?]*/\n&\n/[num];D'

... onde você substituiria o [num] acima por algum número representando a ocorrência desejada.

Se a ocorrência numerada que você especificar não existir, conforme demonstrado no exemplo a seguir, sed simplesmente imprimirá nada.

echo ,2,3 | sed '/\n/P;//d;s/[^,]*/\n&\n/4;D'

Acima da primeira correspondência para uma seqüência de zero ou mais caracteres não-vírgula são os caracteres zero que ocorrem antes da primeira vírgula. O segundo é 2 e o terceiro é 3 - não há quarta ocorrência desse padrão e, portanto, a substituição não é bem-sucedida.

Observe também que nem todo sed suportará o escape \n newline no campo de substituição do lado direito e talvez seja necessário substituir os caracteres n na sequência de escape por novas linhas literais.

sed '/\n/P;//d;s/[^?]*/\
&\
/[num];D'

Com sua string, ele:

str='> Leslie Cheung April 1 ? Elvis August 16 ? Leonard Nimoy February 27'
for o in 1 2 3
do  printf %s\n "$str" |
    sed "/\n/P;//d;s/[^?]*/\n:$o:&\n/$o;D"
done

... que é apenas um pequeno for loop que executa sed 3 vezes tentando todos os 3 [^?]* correspondências e imprime ...

:1:> Leslie Cheung April 1
:2: Elvis August 16
:3: Leonard Nimoy February 27

... ou um para cada valor de $o .

Você pode expandir um pouco para pular as ocorrências de [num] não-nulo, como:

i= 
until [ "$((i+=1))" -gt 10 ] &&
      printf %s\n "$str"
do    printf %s ":$i:$str?"; done |
sed '/..*\n?*/P;s///;s/[^?]*/\n&\n/7;D'

... que imprime ...

:3:> Leslie Cheung April 1
 Elvis August 16
 Leonard Nimoy February 27
:10:> Leslie Cheung April 1

Também pode ser inclusive . Por exemplo:

printf %s\n "$str?$str" |
sed '/.*\n[^_[:alnum:]]*/P;s///
    s/[_[:alnum:]]\{1,\}/\n&\n/3;D'

... que imprime cada uma em uma linha separada a cada terceira seqüência não nula de caracteres alfanuméricos e _ em uma concatenação de duas de suas strings ...

April
August
Nimoy
Leslie
1
16
February
    
por 31.03.2015 / 23:12
2

sed

Você pode usar sed para isso, mas não é aconselhável, por exemplo, aqui está uma solução baseada em zero que usa um quantificador para selecionar o campo desejado:

n=1
sed 's/\([^?]*? *\)\{'$n'\}//; s/?.*//' <<<"$above_string"

Saída:

Elvis August 16 
    
por 31.03.2015 / 20:29