Sostituisci un carattere tranne le ultime x occorrenze


9

Ho un file che ha un sacco di nomi host correlati con IP che assomiglia a questo:

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

Voglio che assomigli a questo:

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

Come posso sostituire il. (punti) dalla prima colonna con - (trattino) per facilitare un ordinamento per la seconda colonna? Stavo pensando di usare sed per sostituire i punti fino al primo spazio, o sostituire tutti i punti tranne gli ultimi tre, ma ho difficoltà a comprendere regex e sed. Posso eseguire semplici rimpiazzi ma questo è un problema!

Questo fa parte di una sceneggiatura più ampia che ho scritto in bash. Sono bloccato in questa parte.

Risposte:


7

Puoi usare AWK

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

Spiegazione

awkdivide una linea sullo spazio bianco per impostazione predefinita. Pertanto, la prima colonna della riga ( $1in awk-ese) sarà quella su cui si desidera eseguire le sostituzioni. A tale scopo, è possibile utilizzare:

 gsub(regex,replacement,string)

per eseguire la sostituzione richiesta.

Si noti che gsubè supportato solo per gawke nawkma su molte distro moderne awkè un collegamento a gawk.


1
+1 Battimi. Penso che una spiegazione sarebbe davvero vantaggiosa per chi ascolta e anche per i futuri lettori.
Joseph R.,

1
@JosephR. Mi dispiace non sono bravo a spiegare ma ho provato e aggiornato ..
Rahul Patil,

2
Le specifiche POSIX per awksi basano nawk, quindi awkdovrebbero avere tutte le implementazioni moderne gsub. Su Solaris, potresti aver bisogno /usr/xpg4/bin/awko nawk.
Stéphane Chazelas,

@RahulPatil Se non ti dispiace, ho aggiunto alcune righe che penso possano aiutare gli altri.
Joseph R.,

@JosephR grazie .., ora sembra perfetto .. :)
Rahul Patil,

6

Se hai bisogno di fare le sostituzioni sul primo campo, la cosa migliore è usare la soluzione awk di Rahul ma fai attenzione che potrebbe influenzare la spaziatura (i campi vengono riscritti con un singolo spazio tra loro).

Puoi evitarlo scrivendolo invece:

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

Il -pflag significa "leggi il file di input riga per riga e stampa ogni riga dopo aver applicato lo script indicato da -e". Quindi, sostituire ( s|pattern|replacement|) la prima sequenza di caratteri non spaziali ( \S+) con il modello corrispondente ( $&) dopo aver sostituito tutto .con -. Il trucco è usare s|||edove l' eoperatore valuterà un'espressione come sostituto. Quindi, puoi avere una sostituzione ( tr/./-/) applicata alla corrispondenza ( $&) della precedente ( s|||e).

Se devi sostituire tutti .con a -tranne gli ultimi 3 ultimi, con GNU sede supponendo che tu abbia un revcomando:

rev file | sed 's/\./-/4g' | rev

1
Si noti che la soluzione Perl presuppone la versione 5.14 o successiva (per /rfunzionare).
Joseph R.,

3

Sed non è lo strumento più semplice per il lavoro - vedi altre risposte per strumenti migliori - ma può essere fatto.

Per sostituire .da -solo fino al primo spazio, l'utilizzo sin un ciclo.

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
'

(Nota che alcune implementazioni di sed non supportano i commenti sulla stessa riga. GNU sed lo fa.)

Per eseguire invece la sostituzione fino all'ultimo spazio:

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
'

Un'altra tecnica utilizza lo spazio di attesa di sed. Salvare il bit che non si desidera modificare nello spazio di attesa, fare il proprio lavoro, quindi richiamare lo spazio di attesa. Qui, ho diviso la linea nell'ultimo spazio e ho sostituito i punti con trattini nella prima 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
'

2

Dal momento che Rahul ti ha dato la risposta canonica per il tuo caso d'uso, ho pensato di provare a rispondere al problema del titolo: sostituire tutto tranne le ultime x occorrenze di una 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

Il codice sopra (testato) non presuppone che tu abbia campi separati da spazio. Sostituirà tutti i punti su una linea con trattini tranne gli ultimi 3 punti. Sostituisci il 3nel codice a tuo piacimento.


2

È possibile utilizzare molti strumenti diversi per questo. Rahul Patil ti ha già dato gawkuno, quindi eccone alcuni altri:

  • perl

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

    L' -aopzione fa sì che perl divida automaticamente le linee di input negli spazi bianchi e salvi i campi risultanti nell'array @F. Il primo campo, quindi, sarà $F[0]quindi sostituiamo ( s///) tutte le occorrenze di .con -nel primo campo e quindi stampiamo l'intero array.

  • conchiglia

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

    Qui, il ciclo while legge il file e si divide automaticamente in spazi bianchi. Questo crea due campi $firste $rest. Il costrutto ${first//pattern/replacement}sostituisce tutte le occorrenze di patterncon replacement.


+1 Mentre perlrun(1)ti dirò che -aè la "modalità autosplit", preferisco pensarla come " awkmodalità": D
Joseph R.,

2

Credo che sia un po 'più facile da leggere di una grande regex cattiva. Fondamentalmente ho appena diviso la linea in due campi nello spazio bianco e uso sed nella prima parte.

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

A seconda della shell, è possibile utilizzare $ {host //./-} invece del comando sed.


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

Senza usare galla fine del comando puoi farlo ... Questo sostituirà semplicemente la prima occorrenza del modello

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.