Remplacer la string après le dernier point dans bash

J'ai le model suivant dans une string (une adresse IP):

123.444.888.235 

Je veux replace le dernier numéro après le point par 0 , alors il devient:

 123.444.888.0 

Comment pourrais-je le faire dans bash ou un autre langage de script shell?

Dans n'importe quel shell POSIX:

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

${var%pattern} est un opérateur introduit par ksh dans les années 80, standardisé par POSIX pour le langage sh standard et maintenant implémenté par tous les shells qui interprètent ce langage, y compris bash .

${var%pattern} développe le contenu de $var dépouillé de la string la plus courte qui correspond à la fin du pattern (ou au même que $var si ce model ne correspond pas). Donc ${var%.*} (Où .* Est un motif qui signifie point suivi d'un nombre quelconque de caractères) se développe à $var sans le plus à droite . et ce qui suit. En revanche, ${var%%.*} Où la string la plus longue qui correspond au model est supprimée s'élargira à $var sans le plus à gauche . et ce qui suit.

Cela devrait fonctionner.

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

Notez que le dernier champ de substitution sed , \10 , est le premier motif apparié ( \1 ), concaténé avec un zéro littéral.

Quelques façons (toutes supposent que vous voulez changer le dernier set de nombres dans la string):

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

Ici, -F'.' dit awk à utiliser . comme séparateur de champs d'input et -vOFS='.' pour l'utiliser comme séparateur de champ de sortie. Ensuite, nous définissons simplement le dernier champ ( $NF ) sur 0 et imprimons la ligne ( 1; abréviation awk pour «imprimer la ligne en cours»).

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

Le -p indique à perl d'imprimer chaque ligne d'input après avoir appliqué le script donné par -e . Le script lui-même est juste un simple opérateur de substitution qui replacea un ou plusieurs nombres à la fin de la ligne par 0 .

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

La même idée dans sed , en utilisant [0-9] au lieu de \d .


Si votre string est dans une variable et que vous utilisez un shell qui supporte ici les strings (comme bash ou zsh par exemple), vous pouvez changer le ci-dessus en:

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

Cas général d'application d'un masque de réseau à une adresse IP:

Étant donné que votre input est une adresse IP et que vous remplacez le dernier octet de cette adresse par .0 , je suppose que ce que vous essayez vraiment d'get est de calculer la partie réseau de l'adresse IP en utilisant le 255.255.255.0 masque de réseau.

Il suffit de replace les octets par des zéros si la longueur de votre masque est divisible par 8, mais ce n'est pas le cas général. Si vous avez besoin d'effectuer cette opération pour n'importe quel masque de réseau valide (sous-réseau), vous pouvez faire quelque chose comme ceci:

 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 

Cela définit 3 fonctions:

  • d2i() convertit la forme décimale pointée d'une adresse IP (ou masque) en un entier simple
  • i2d() fait le contraire – convertit un entier simple en un point décimal en pointillés
  • ipmask() calcule simplement un bit ET d'une adresse et d'un masque de réseau pour donner la partie réseau d'une adresse. Bash s'attend à ce que les opérandes de & soient des entiers.

Les deux appels à ipmask montrent comment le réseau peut être calculé à partir d'une adresse IP pour deux masques différents.


Note comme indiqué dans la question, 123.444.888.235 est une adresse IP non valide. J'ai utilisé 123.44.88.235 place de ces exemples.

Une réponse sed alternative:

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

Il regroupe tout jusqu'au dernier point et remplace la ligne complète par le contenu du groupe suivi de 0.
J'utilise -r pour éviter d'avoir à échapper aux parenthèses.