Manipolazione del testo con sed


12

Attualmente, ho più file di testo con contenuti simili a questo (con molte righe):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Vorrei cambiare ogni riga per avere il seguente formato:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

C'è un modo di fare quanto sopra usando sed? O devo ricorrere a Python?

Risposte:


22

Potresti farlo con sed, sì, ma altri strumenti sono più semplici. Per esempio:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Spiegazione

awk dividere ogni linea di ingresso su uno spazio (default), risparmiando ogni singolo settore come $1, $2,$N . Così:

  • printf "%s ", $2; stamperà il 2 ° campo e uno spazio finale.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: ripeterà i campi 3 fino all'ultimo campo ( NFè il numero di campi) e per ciascuno di essi stamperà il primo campo, a :, quindi il campo corrente e un:1 .
  • print "" : questo stampa solo una riga finale.

O Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Spiegazione

Le -amarche si perlcomportano come awke dividono i loro input su spazi bianchi. Qui, i campi sono memorizzati nell'array @F, il che significa che il 1 ° campo sarà $F[0], il 2 ° $F[1]ecc. Quindi:

  • print "$F[1] " : stampa il 2 ° campo.
  • print "$F[0]:$_:1 " for @F[2..$#F];: iterare sui campi 3 fino all'ultimo campo ( $#Fè il numero di elementi nella matrice @F, quindi @F[2..$#F]prende una fetta della matrice a partire dal 3 ° elemento fino alla fine della matrice) e stampa il 1 ° campo, a :, quindi il campo corrente e a :1.
  • print "\n" : questo stampa solo una riga finale.

12

Ecco un orribile sed modo!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Più facilmente:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

Appunti

  • -r usa ERE
  • s/old/new/sostituire oldconnew
  • ^([0-9]+) salva alcuni numeri all'inizio della riga
  • \1 riferimento al primo modello salvato
  • :a etichetta questa sezione dello script a
  • ( |$) uno spazio o la fine della linea
  • t verifica se l'ultima sostituzione ha avuto esito positivo; in caso affermativo, esegui il comando successivo
  • atrova l'etichetta :ae fallo di nuovo
  • s/ $// rimuovere lo spazio finale

Quindi, dopo aver aggiunto la struttura alla prima parte, troviamo ripetutamente l'ultima istanza della struttura e la applichiamo al numero successivo ...

Ma sono d'accordo che altri strumenti lo rendono più facile ...


Stavo aspettando la tua soluzione sed: D
Ravexina,

: D mi ci è voluto un po '@Ravexina - Credo che Muru possa renderne uno più pulito
Zanna,

5

Con awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

o con bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

Produzione:

0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
1 564: 7: 1 564: 12: 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 

5

Bene, puoi farlo in sed, ma funziona anche Python.

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

I contenuti reformatfile.pysono come segue:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

Come funziona? Non c'è davvero niente di particolarmente speciale. Apriamo il primo argomento della riga di comando come file per la lettura e procediamo con la suddivisione di ogni riga in "parole" o singoli elementi. Le prime parole diventano prefvariabili e stampiamo sul secondo elemento stdout (parole [1]) che termina con lo spazio. Successivamente costruiamo un nuovo insieme di "parole" tramite la comprensione e la .join()funzione dell'elenco su un elenco temporaneo di pref, ogni parola e stringa "1". L'ultimo passo è quello di stampare quelli


4

Con awk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

Si tratta di formattare i campi separati da spazio nel formato desiderato:

  • printf("%s ", $2) stampa il secondo campo con uno spazio finale

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) scorre tra il 3o e il 2o ultimo campo e stampa i campi nel formato desiderato (primo campo, quindi due punti, quindi il campo corrente, quindi due punti, infine 1) con uno spazio finale

  • printf("%s:%s:1\n", $1, $NF) stampa l'ultimo campo con newline

Esempio:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
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.