Substituir string após o último ponto no bash

8

Eu tenho o seguinte padrão em uma string (um endereço IP):

123.444.888.235

Eu quero substituir o último número após o ponto com 0 , então ele se torna:

123.444.888.0

Como eu poderia fazer isso em bash ou outra linguagem de script de shell?

    
por KeepMove 21.12.2015 / 17:13

5 respostas

16

Em qualquer shell POSIX:

var=123.444.888.235
new_var="${var%.*}.0"

${var%pattern} é um operador introduzido por ksh nos anos 80, padronizado por POSIX para a linguagem sh padrão e agora implementado por todos os shells que interpretam esse idioma, incluindo bash .

${var%pattern} expande para o conteúdo de $var da linha mais curta que corresponde a padrão no final dela (ou para o mesmo que $var se esse padrão não corresponde). Portanto, ${var%.*} (onde .* é um padrão que significa ponto seguido por qualquer número de caracteres) se expande para $var sem o coentem% mais à direita e o que segue. Por contraste, . , em que a sequência mais longa que corresponde ao padrão, seria expandida para ${var%%.*} sem a coentidade% mais à esquerda e o que a segue.

    
por 21.12.2015 / 17:28
8

Isso deve funcionar.

echo 123.444.888.235 | sed 's/\([0-9]*\.[0-9]*\.[0-9]*\.\)[0-9]*//'

Observe que o último campo de sed de substituição, , é o primeiro padrão correspondente ( ), concatenado com um zero literal.

    
por 21.12.2015 / 17:18
5

Algumas maneiras (todas assumem que você deseja alterar o último conjunto de números na string):

$ echo 123.444.888.235 | awk -F'.' -vOFS='.' '{$NF=0}1;'
123.444.888.0

Aqui, -F'.' diz a awk para usar . como separador de campos de entrada e -vOFS='.' para usá-los como o separador de campos de saída. Então, simplesmente definimos o último campo ( $NF ) para 0 e imprimimos a linha ( 1; é awk abreviação para "imprimir a linha atual").

$ echo 123.444.888.235 | perl -pe 's/\d+$/0/'
123.444.888.0

O -p informa perl para imprimir cada linha de entrada depois de aplicar o script fornecido por -e . O script em si é apenas um operador de substituição simples que substituirá um ou mais números no final da linha com 0 .

$ echo 123.444.888.235 | sed 's/[0-9]*$/0/'
123.444.888.0

A mesma ideia em sed , usando [0-9] em vez de \d .

Se a sua string estiver em uma variável e você usar um shell que suporte aqui strings ( como bash ou zsh , por exemplo), você pode alterar o acima para:

awk -F'.' -vOFS='.' '{$NF=0}1;' <<<$var
perl -pe 's/\d+$/0/' <<<$var
sed 's/[0-9]*$/0/' <<<$var
    
por 21.12.2015 / 17:36
5

Caso geral para aplicar uma máscara de rede a um endereço IP:

Dado que sua entrada é um endereço IP, e que você está substituindo o último octeto desse endereço por .0 , então eu assumo que o que você está realmente tentando alcançar é calcular a rede parte do endereço IP, usando o 255.255.255.0 netmask.

Simplesmente substituir octetos por zeros é OK se o tamanho da sua máscara de rede for divisível por 8, mas este não é o caso geral. Se você precisar executar essa operação para qualquer máscara de rede válida (subnet), poderá fazer algo assim:

function d2i() {
    echo $(( 0x$( printf "%02x" ${1//./ } ) ))
}

function i2d() {
    h=$( printf "%08X" "$1" )
    echo $(( 0x${h:0:2} )).$(( 0x${h:2:2} )).$(( 0x${h:4:2} )).$(( 0x${h:6:2} ))
}

function ipmask() {
    i2d $(( $( d2i $1 ) & $( d2i $2 ) ))
}

ipmask 123.44.88.235 255.255.255.0     # outputs 123.44.88.0
ipmask 123.44.88.235 255.255.255.240   # outputs 123.44.88.224

Isso define 3 funções:

  • d2i() converte a forma decimal pontuada de um endereço IP (ou máscara) em um inteiro simples
  • i2d() faz o oposto - converte um inteiro simples em um decimal pontuado
  • ipmask() simplesmente calcula um AND bit a bit de um endereço e uma máscara de rede para fornecer a parte da rede de um endereço. Bash espera que os operandos de & sejam inteiros.

As duas chamadas para ipmask mostram como a rede pode ser calculada a partir de um endereço IP para duas máscaras diferentes.

Observe que, conforme indicado na pergunta, 123.444.888.235 é um endereço IP inválido. Eu usei 123.44.88.235 para esses exemplos.

    
por 22.12.2015 / 00:18
0

Uma alternativa sed answer:

sed -r 's/(.*\.).*//'

Agrupa tudo até o último ponto e substitui a linha completa com o conteúdo do grupo seguido por 0.
Eu uso -r para evitar ter que escapar de parênteses.

    
por 22.12.2015 / 13:59