Como evitar que um padrão de inserção usando sed seja aplicado no meio de uma string?

2

Objetivo

O objetivo é converter a seguinte string:

hello_hello,123-world567-helloworld123456,world1234-hello09876

usando sed em um formato específico.

Tentativas

sed -e 's|^\(hello_[a-z0-9]\{3\}\)\(.*\)|,|g;s|..|&/|g' /tmp/file

Resultado esperado

he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76/

Resultado atual

O problema é que cada 2 caracteres a / é inserido. A inserção de / deve ser evitada na parte que reside entre as duas vírgulas.

he/ll/o_/he/ll/o,/12/3-/wo/rl/d5/67/-h/el/lo/wo/rl/d1/23/45/6,/wo/rl/d1/23/4-/he/ll/o0/98/76/
    
por 030 29.12.2014 / 18:36

2 respostas

3

Eu posso fazer assim:

sed 's|\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|/|g
' <<\IN                                     
hello_hello,123-world567-helloworld123456,world1234-hello09876
IN

... que imprime ...

/he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76

Então, mais das alterações feitas são feitas na segunda s/// ubstitution - mas isso é porque eu removi todas as primeiras.

Portanto, a maior parte do seu problema foi que você estava simplesmente dizendo sed para substituir em / após cada dois caracteres - o . dots significa qualquer char e o g significa global - ou todos .

A segunda maior parte foi que a primeira substituição não estava ajudando você - e foi completamente desnecessária.

Mais do que isso, você também estava inserindo uma vírgula extra na primeira substituição - então, depois que eu consegui acertar o primeiro bit, eu ainda estava correndo em campos extras. Olhe:

\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|/

Essa é a declaração de substituição que funcionou para mim e é por isso:

  • \(,[^,]*,\)\{0,1\} - em um contexto global, você tem que ter cuidado para obter apenas o quanto precisar. Você estava substituindo cada dois caracteres e é isso que você tem - sed é ganancioso . Isso é referenciado primeiro - o que é importante - porque como sed lê da esquerda para a direita, ele normalmente apenas insere uma barra entre cada dois caracteres sequenciais e não de vírgula, mas se encontrar uma vírgula, ela será lida até a próxima. e salve o bloco inteiro em sem inserir nenhuma barra.

  • \([^,]\{1,2\}\) - Você não pode usar os . dots aqui - eles corresponderão a uma vírgula e você acabará escrevendo nas barras depois de pular um delimitador. Você precisa excluir explicitamente as vírgulas. E é isso que isso faz - cada sequência de 1 ou 2 delas -, embora sed sempre traga o maior desses números possíveis.

Uma diferença que eu posso ver entre isso e aquilo em seu exemplo é que a primeira barra aqui está na cabeça da string e não há barra final, enquanto a sua faz o oposto. Para remediar isso, conforme necessário:

...;s|^/\(.*/.\)/*$|/|...
    
por 29.12.2014 / 19:15
1

Tenho certeza de que alguém criará uma abordagem sed pura, mas acho que usar um programa que entenda campos de entrada, e não apenas linhas, é muito mais fácil para esse tipo de coisa:

  1. Perl

    $ perl -F, -lane 'for($F[0],$F[2]){s|(..)|/|g;} print join ",",@F' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    Explicação

    • -a : divida cada linha de entrada em campos e salve-os na matriz @F . O campo 1dt será $F[0] , o segundo $F[1] e assim por diante.
    • -F : defina o delimitador de campo para , .
    • -n e -e : leia cada linha de entrada ( -n ) e aplique o script fornecido por -e .
    • -l : remova as novas linhas iniciais e adicione um \n a cada print de chamada.
    • for($F[0],$F[2]){} : aplica isso ao primeiro e terceiro campos.
    • s|(..)|/|g; : substituição simples, ele adicionará / após cada segundo caractere.
    • print join ",",@F' : junte a lista de campos com vírgulas e imprima. Como os campos já foram alterados na etapa anterior, isso imprimirá os alterados.
  2. GNU awk

    $ awk -F, -v OFS="," '{$1=gensub(/(..)/,"\1/","g",$1); $3=gensub(/(..)/,"\1/","g",$3);}1;' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    Explicação

    Como acima, o -F define o delimitador de campo. -v OFS="," define o delimitador de saída como , . Então, a função gensub() (apenas GNU awk, acredito) executa a substituição. Aqui, ele é executado no primeiro e terceiro campos.

por 29.12.2014 / 19:04

Tags