Reemplazar cadena después del último punto en bash

12

Tengo el siguiente patrón en una cadena (una dirección IP):

123.444.888.235

Quiero reemplazar el último número después del punto con 0, por lo que se convierte en:

123.444.888.0

¿Cómo podría hacerlo en bashotro lenguaje de script de shell?

Ari
fuente
1
¿Dónde están esas cuerdas? En una variable bash? ¿En un archivo de texto uno por línea?
Stéphane Chazelas
Está en una variable. Lo siento, se olvidó de mencionarlo.
Ari
3
Si se trata de direcciones IP, 444 y 888 son valores de octeto no válidos.
Trauma digital

Respuestas:

24

En cualquier shell POSIX:

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

${var%pattern}es un operador introducido por kshlos años 80, estandarizado por POSIX para el shlenguaje estándar y ahora implementado por todos los shells que interpretan ese idioma, incluido bash.

${var%pattern}se expande al contenido $vardespojado de la cadena más corta que coincide con el patrón al final (o al mismo como $varsi ese patrón no coincidiera). Entonces ${var%.*}(donde .*hay un patrón que significa punto seguido de cualquier número de caracteres) se expande hasta el $varextremo derecho .y lo que le sigue. Por el contrario, ${var%%.*}donde se despoja la cadena más larga que coincide con el patrón se expandiría $varsin el extremo izquierdo .y lo que sigue.

Stéphane Chazelas
fuente
¿Qué es ? ${var%.*}
voces
@ tjt263, en la línea de comando escriba man bashy presione enter, luego escriba /suffix patterny presione enter. :)
Comodín
Se agregaron algunas citas después de su publicación : new_var="${var%.*}.0"... nJoy !.
8

Esto debería funcionar.

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

Tenga en cuenta que el último campo de sedsustitución, \10es el primer patrón coincidente ( \1), concatenado con un cero literal.

Kira
fuente
5

Algunas formas (todas suponen que desea cambiar el último conjunto de números en la cadena):

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

Aquí, -F'.'indica awkque se use .como separador de campo de entrada y -vOFS='.'que se use como separador de campo de salida. Luego, simplemente establecemos el último campo ( $NF) en 0 e imprimimos la línea ( 1;es la awkabreviatura de "imprimir la línea actual").

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

El -ple dice perlque imprima cada línea de entrada después de aplicar el script dado por -e. El script en sí es solo un simple operador de sustitución que reemplazará uno o más números al final de la línea 0.

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

La misma idea en sed, usando en [0-9]lugar de \d.


Si su cadena está en una variable y utiliza un shell que admite cadenas aquí (como basho zshpor ejemplo), puede cambiar lo anterior a:

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

Caso general para aplicar una máscara de red a una dirección IP:

Dado que su entrada es una dirección IP y que está reemplazando el último octeto de esa dirección .0, supongo que lo que realmente está tratando de lograr es calcular la porción de red de la dirección IP, usando la 255.255.255.0máscara de red.

Simplemente reemplazar octetos con ceros está bien si la longitud de su máscara de red es divisible por 8, pero este no es el caso general. Si alguna vez necesita realizar esta operación para cualquier máscara de red (subred) válida, puede hacer algo como esto:

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

Esto define 3 funciones:

  • d2i() convierte la forma decimal punteada de una dirección IP (o máscara) en un entero simple
  • i2d() hace lo contrario: convierte un entero simple en un decimal punteado
  • ipmask()simplemente calcula un AND a nivel de bit de una dirección y una máscara de red para dar la porción de red de una dirección. Bash espera que los operandos de &sean enteros.

Las dos llamadas ipmaskmuestran cómo se puede calcular la red a partir de una dirección IP para dos máscaras diferentes.


Tenga en cuenta que como se indica en la pregunta, 123.444.888.235es una dirección IP no válida. En su lugar, he usado 123.44.88.235para estos ejemplos.

Trauma digital
fuente
0

Una sedrespuesta alternativa :

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

Agrupa todo hasta el último punto y reemplaza la línea completa con el contenido del grupo seguido de 0.
Uso -r para evitar tener que escapar del paréntesis.

Aaron
fuente