Substituir um caractere, exceto as últimas x ocorrências

7

Eu tenho um arquivo que tem um monte de nomes de host correlacionados com IPs que se parece com isso:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

Eu quero que seja assim:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

Como posso substituir o. (pontos) da primeira coluna com - (hífen) para facilitar uma classificação pela segunda coluna? Eu estava pensando em usar sed para substituir pontos até o primeiro espaço, ou substituir todos os pontos, mas os últimos três, mas estou tendo problemas para entender regex e sed. Eu posso fazer substituições simples, mas isso está muito acima da minha cabeça!

Isso faz parte de um script maior que escrevi no bash. Estou preso nesta parte.

    
por Florin 28.01.2014 / 16:27

7 respostas

6

Você pode usar o AWK

awk '{gsub(/-/,".",$1);print}' infile

Explicação

awk divide uma linha no espaço em branco por padrão. Assim, a primeira coluna da linha ( $1 in awk -ese) será aquela em que você deseja realizar as substituições. Para este propósito, você pode usar:

 gsub(regex,replacement,string)

para realizar a substituição necessária.

Observe que gsub é suportado apenas para gawk e nawk , mas em muitas distribuições modernas awk é um link para gawk .

    
por 28.01.2014 / 16:32
6

Se você precisar fazer as substituições no primeiro campo, o melhor é usar a solução awk do Rahul mas cuidado pode afetar o espaçamento (os campos são reescritos com um único espaço entre eles).

Você pode evitar isso escrevendo:

perl -pe 's|\S+|$&=~tr/./-/r|e' file

O sinalizador -p significa "leia o arquivo de entrada linha a linha e imprima cada linha depois de aplicar o script fornecido por -e ". Em seguida, substitua ( s|pattern|replacement| ) a primeira seqüência de caracteres não espaciais ( \S+ ) pelo padrão correspondente ( $& ) após substituir todos os . por - . O truque é usar s|||e , em que o operador e avaliará uma expressão como uma substituição. Assim, você pode ter uma substituição ( tr/./-/ ) aplicada à correspondência ( $& ) da anterior ( s|||e ).

Se você precisar substituir cada . por - , exceto os últimos 3 últimos, com GNU sed e supondo que você tenha um comando rev :

rev file | sed 's/\./-/4g' | rev
    
por 28.01.2014 / 16:52
3

Sed não é a ferramenta mais fácil para o trabalho - veja outras respostas para melhores ferramentas - mas isso pode ser feito.

Para substituir . por - apenas até o primeiro espaço, use s em um loop.

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(Note que algumas implementações de sed não suportam comentários na mesma linha. O GNU sed tem.)

Para executar a substituição até o último espaço:

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

Outra técnica faz uso do espaço de espera do sed. Salve o bit que você não deseja modificar no espaço de armazenamento, faça seu trabalho e, em seguida, recupere o espaço de armazenamento. Aqui, eu divido a linha no último espaço e substituo os pontos por traços na primeira parte.

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'
    
por 29.01.2014 / 02:10
2

Como a Rahul forneceu a a resposta canônica para o seu caso de uso, pensei em fazer uma tentativa respondendo ao problema do titular: substituindo todas as últimas ocorrências de x de um regex:

perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

O código acima (testado) não supõe que você tenha campos separados por espaços. Ele irá substituir todos os pontos em uma linha com traços, exceto os últimos 3 pontos. Substitua o 3 no código ao seu gosto.

    
por 28.01.2014 / 16:44
2

Você pode usar muitas ferramentas diferentes para isso. Rahul Patil já lhe deu um gawk , então aqui estão alguns outros:

  • perl

    perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    A opção -a faz com que o perl divida automaticamente as linhas de entrada no espaço em branco e salve os campos resultantes na matriz @F . O primeiro campo, portanto, será $F[0] , então substituímos ( s/// ) todas as ocorrências de . por - no primeiro campo e depois imprimimos a matriz inteira.

  • shell

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    Aqui, o loop while lê o arquivo e automaticamente divide o espaço em branco. Isso cria dois campos, $first e $rest . A construção ${first//pattern/replacement} substitui todas as ocorrências de pattern por replacement .

por 28.01.2014 / 16:49
2

Eu acredito que isso é um pouco mais fácil de ler do que um regex desagradável grande. Basicamente eu apenas divido a linha em dois campos no espaço em branco e uso sed na primeira parte.

while read -r host ip; do
    echo "$(sed 's/\./-/g' <<< "$host") $ip"
done < input_file

Dependendo do seu shell, você também pode usar $ {host //./-} em vez do comando sed.

    
por 28.01.2014 / 16:42
0
sed 's/\./-/' <file name>

Sem usar g no final do comando, você pode fazer isso ... Isso simplesmente substituirá a 1ª ocorrência do padrão

    
por 31.12.2014 / 14:19