Por que o sed imprime a primeira string do arquivo, mesmo que não encontre grupos correspondentes

4

Estou tentando escrever uma expressão sed que irá extrair grupos correspondentes do arquivo.

Por exemplo Eu tenho folha .cue e quero extrair sua tag de gênero. Tem o seguinte formato:

$ cat file.cue | grep GENRE
REM GENRE "POP"

Eu criei a expressão sed para extrair esta tag e substituir aspas em torno dela:

$  sed -e 's/REM[ \t]*GENRE[ \t]*\(.*\)//;s/"\(.*\)"//;q' file.cue 
POP

Minha pergunta é por que, se não for possível encontrar o grupo correspondente, apenas será impressa a primeira string?

$  sed -e 's/REMerror[ \t]*GENRE[ \t]*\(.*\)//;s/"\(.*\)"//;q' file.cue 
REM GENRE POP

Exemplo de folha de sugestões:

REM GENRE "POP"
REM DATE 2012
REM DISCID EC10BD10
REM COMMENT "ExactAudioCopy v0.99pb5"
PERFORMER "JAM Project"
TITLE "JAM Project BEST COLLECTION IX THE MONSTERS"
FILE "LACA-15250.flac" WAVE
  TRACK 01 AUDIO
    TITLE "THE MONSTERS"
    PERFORMER "JAM Project"
    INDEX 01 00:00:00
    
por diffycat 18.01.2014 / 21:59

2 respostas

4

Seu script sed , como está, sempre imprimirá a primeira linha, independentemente de ter sido modificada. A ação padrão para sed é imprimir seu espaço de padrão (linha de entrada atual 1 ) como padrão após a aplicação de todos os outros comandos.

Então, basicamente, o que você está mandando fazer é aplicar algumas substituições e depois sair. Isso significa que ele tentará suas substituições na primeira linha e as imprimirá após as substituições (independentemente de terem sido bem-sucedidas), já que essa é a ação padrão. Em seguida, ele encontra q e sai completamente do processamento.

Se você quiser suprimir a ação de impressão padrão, precisará da opção -n , mas precisará instruir explicitamente sed a imprimir as linhas em que a substituição foi bem-sucedida:

sed -n 's/REM[ \t]*GENRE[ \t]*"\(.*\)"//p;q' your_file_here

Agora, este script imprimirá a primeira linha em que a substituição foi bem-sucedida e, em seguida, sairá.

Note que você não precisa de duas substituições, apenas uma é suficiente.

Nota lateral não relacionada

Não há necessidade de

cat your_file | grep 'foobar'

quando

grep 'foobar' your_file

faria.

1 Nem sempre a linha de entrada atual: você pode, por exemplo, adicionar linhas ao espaço padrão.

    
por 18.01.2014 / 22:47
3

Se você não se opuser a usar grep , e grep , por acaso, for a implementação GNU construída com o suporte PCRE, você poderá usar suas instalações PCRE (Perl Compatible Regular Expressions) .

$ grep -oP '.*(?<=GENRE ").*(?=")' file.cue 
POP

O texto acima procura a string ...GENRE " à direita de .* seguido por " à direita. Você pode aumentar o .* para ser \w+ se todos forem caracteres de palavras, por exemplo.

O que há de errado com sed?

O problema é que você teve 2 pesquisas e substituições no seu exemplo.

  • s/REMerror[ \t]*GENRE[ \t]*\(.*\)//;
  • s/"\(.*\)"//;

O primeiro não combina com nada e não faz nada. O segundo corresponde à palavra com aspas duplas "POP" e retorna o conteúdo, POP , formando a string que você vê,

REM GENRE POP

Você pode se convencer de que isso é o que está acontecendo fazendo a primeira pesquisa.

$ sed -e 's/"\(.*\)"//;q' file.cue 
REM GENRE POP
    
por 18.01.2014 / 22:22