Usando sed para substituir espaços em arquivos de texto com _ somente quando entre “” após uma string específica

1

Como posso usar sed para substituir um espaço por um _ somente quando ele ocorre em um arquivo de texto após a string title= e entre " "

Por exemplo (linhas em um arquivo de texto):

title="This is the title of my book" img=scr " </header><!-- .entry-header -->
title="Today is a beautiful day" img=scr " </header><!-- .entrrrrkkky-header -->

Arquivo de texto modificado desejado após o sed:

title="This_is_the_title_of_my_book" img=scr " </header><!-- .entry-header -->
title="Today_is_a_beautiful_day" img=scr " </header><!-- .entrrrrkkky-header -->

Basicamente, o espaço só seria substituído por _ quando ocorre entre o " " após a string title=

O nome do arquivo de texto é arbitrário - digamos file.txt

    
por speld_rwong 10.04.2016 / 03:03

1 resposta

2

Você teria que fazer isso em um loop:

s/\(^.*title="[^" ]*\) \([^"]*".*$\)/_/

ou (mais rápido)

s/\(title="[^" ]*\) \([^"]*"\)/_/

e use o recurso de teste e ramificação de sed, tentando novamente o substituto até que nenhuma outra alteração seja feita. O ponto dos padrões neste comando é dividir a linha no primeiro (restante) espaço e substituir esse espaço por um sublinhado.

Aqui está um script:

#!/bin/sh
sed     -e ':loop' \
        -e 's/\(title="[^" ]*\) \([^"]*"\)/_/' \
        -e 't loop' <foo.in >foo.out
diff -u foo.in foo.out

A resposta inicial usou um padrão mais amplo, mas @ g-man comentou que não era necessário. Foi mais lento, como ilustrado pelo timing sed em um arquivo de 10Mb (testado com o GNU sed no Debian 7):

$ ./foo1
27.03user 0.01system 0:27.18elapsed 99%CPU (0avgtext+0avgdata 1104maxresident)k
0inputs+0outputs (0major+333minor)pagefaults 0swaps
9.54user 0.00system 0:09.60elapsed 99%CPU (0avgtext+0avgdata 972maxresident)k
0inputs+0outputs (0major+301minor)pagefaults 0swaps

Com o OSX, a diferença não é tanto:

$ ./foo1

real    0m11.943s
user    0m11.897s
sys     0m0.024s

real    0m5.858s
user    0m5.839s
sys     0m0.014s

Curiosamente, o padrão mais amplo não funciona com o sed do Solaris (mas o menor faz). Ele não corresponde a nenhum dos extremos da linha dentro do agrupamento \( e \) , enquanto o BSD e o GNU fazem isso. Da mesma forma, funciona com o HPUX 11.31 e o AIX 7.1

O POSIX sed usa BREs e o recurso de agrupamento é coberto por 9.3.6 BREs que correspondem a vários caracteres :

A subexpression can be defined within a BRE by enclosing it between the character pairs "\(" and "\)". Such a subexpression shall match whatever it would have matched without the "(" and ")", except that anchoring within subexpressions is optional behavior; see BRE Expression Anchoring. Subexpressions can be arbitrarily nested.

9.3.8 Fixação da Expressão BRE explica o termo:

A BRE can be limited to matching strings that begin or end a line; this is called "anchoring".

Portanto, no contexto do padrão à medida que é implementado, essa é uma limitação conhecida do Solaris sed , que o padrão permite como comportamento "opcional".

Leitura adicional:

por 10.04.2016 / 03:34