Remplacer un caractère sauf les dernières occurrences x

9

J'ai un fichier qui a un tas de noms d'hôtes corrélés avec des IP qui ressemble à ceci:

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

Je veux que ça ressemble à ceci:

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

Comment puis-je remplacer le. (points) de la première colonne avec - (trait d'union) afin de faciliter un tri par la deuxième colonne? Je pensais utiliser sed pour remplacer les points jusqu'au premier espace, ou remplacer chaque point sauf les trois derniers, mais j'ai du mal à comprendre regex et sed. Je peux effectuer des remplacements simples mais c'est bien au-dessus de ma tête!

Cela fait partie d'un script plus large que j'ai écrit en bash. Je suis coincé dans cette partie.

Florin
la source

Réponses:

7

Vous pouvez utiliser AWK

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

Explication

awkdivise une ligne sur les espaces par défaut. Ainsi, la première colonne de la ligne ( $1en awk-ese) sera celle sur laquelle vous souhaitez effectuer les substitutions. A cet effet, vous pouvez utiliser:

 gsub(regex,replacement,string)

pour effectuer la substitution requise.

Notez que gsubn'est pris en charge que pour gawket nawkmais sur de nombreuses distributions modernes awkest un lien logiciel vers gawk.

Rahul Patil
la source
1
+1 Battez-moi. Je pense qu'une explication serait également très utile au demandeur et aux futurs lecteurs.
Joseph R.
1
@JosephR. Désolé, je ne suis pas bon en explication, mais j'ai essayé et mis à jour ..
Rahul Patil
2
La spécification POSIX pour awkest basée sur nawk, donc toutes les awkimplémentations modernes devraient avoir gsub. Sous Solaris, vous pouvez avoir besoin de /usr/xpg4/bin/awkou nawk.
Stéphane Chazelas
@RahulPatil Si cela ne vous dérange pas, j'ai ajouté quelques lignes qui, je pense, aideraient les autres.
Joseph R.
@JosephR merci .., il semble parfait maintenant .. :)
Rahul Patil
6

Si vous devez effectuer les substitutions sur le premier champ, le mieux est d'utiliser la solution awk de Rahul mais attention, cela peut affecter l'espacement (les champs sont réécrits avec un seul espace entre eux).

Vous pouvez l'éviter en l'écrivant à la place:

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

Le -pdrapeau signifie "lire le fichier d'entrée ligne par ligne et imprimer chaque ligne après avoir appliqué le script donné par -e". Ensuite, remplacez ( s|pattern|replacement|) la première séquence de caractères non-espace ( \S+) par le motif correspondant ( $&) après avoir remplacé tous .par -. L'astuce consiste à utiliser s|||eoù l' eopérateur évaluera une expression comme remplacement. Ainsi, vous pouvez avoir un remplacement ( tr/./-/) appliqué à la correspondance ( $&) de la précédente ( s|||e).

Si vous devez remplacer chaque .par un -sauf les 3 derniers, par GNU sedet en supposant que vous avez une revcommande:

rev file | sed 's/\./-/4g' | rev
Stéphane Chazelas
la source
1
Notez que la solution Perl suppose la version 5.14 ou supérieure (pour que cela /rfonctionne).
Joseph R.
3

Sed n'est pas l'outil le plus simple pour le travail - voir d'autres réponses pour de meilleurs outils - mais cela peut être fait.

Pour remplacer .par -seulement au premier espace, l' utilisation sdans une boucle.

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

(Notez que certaines implémentations sed ne prennent pas en charge les commentaires sur la même ligne. GNU sed le fait.)

Pour effectuer à la place le remplacement jusqu'au dernier espace:

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

Une autre technique utilise l'espace de retenue de sed. Enregistrez le bit que vous ne souhaitez pas modifier dans l'espace d'attente, effectuez votre travail, puis rappelez l'espace d'attente. Ici, je divise la ligne au dernier espace et remplace les points par des tirets dans la première partie.

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
'
Gilles 'SO- arrête d'être méchant'
la source
2

Depuis que Rahul vous a donné la réponse canonique pour votre cas d'utilisation, j'ai pensé essayer de répondre au problème titulaire: remplacer toutes les occurrences x, sauf les dernières x, d'une expression régulière:

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

Le code ci-dessus (testé) ne suppose pas que vous avez des champs séparés par des espaces. Il remplacera tous les points d'une ligne par des tirets à l'exception des 3 derniers points. Remplacez le 3dans le code à votre convenance.

Joseph R.
la source
2

Vous pouvez utiliser de nombreux outils différents pour cela. Rahul Patil vous en a déjà donné gawkun alors voici quelques autres:

  • perl

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

    Le -acommutateur fait que perl divise automatiquement les lignes d'entrée sur les espaces et enregistre les champs résultants dans le tableau @F. Le premier champ sera donc $F[0]ainsi nous remplaçons ( s///) toutes les occurrences de .avec -dans le premier champ, puis imprimons le tableau entier.

  • coquille

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

    Ici, la boucle while lit le fichier et se divise automatiquement en espace blanc, ce qui crée deux champs, $firstet $rest. La construction ${first//pattern/replacement}remplace toutes les occurrences de patternavec replacement.

terdon
la source
+1 Alors que perlrun(1)je vais vous dire que -ac'est du "mode autosplit", je préfère le considérer comme " awkmode": D
Joseph R.
2

Je crois que c'est un peu plus facile à lire qu'un gros regex méchant. Fondamentalement, je viens de diviser la ligne en deux champs à l'espace blanc et d'utiliser sed sur la première partie.

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

Selon votre shell, vous pouvez également utiliser $ {host //./-} au lieu de la commande sed.

maedox
la source
0
sed 's/\./-/' <file name>

Sans utiliser gà la fin de la commande, vous pouvez le faire… Cela remplacera simplement la 1ère occurrence du motif

sunandan
la source