it-swarm.com.de

Textbearbeitung mit sed

Derzeit habe ich mehrere Textdateien mit folgendem Inhalt (mit vielen Zeilen):

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

Ich möchte jede Zeile so ändern, dass sie das folgende Format hat:

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

Gibt es eine Möglichkeit, dies mit sed zu tun? Oder muss ich auf Python zurückgreifen?

12
user695634

Sie könnten es mit sed tun, aber andere Werkzeuge sind einfacher. Zum Beispiel:

$ 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 

Erläuterung

awk teilt jede Eingabezeile in Leerzeichen (standardmäßig) und speichert jedes Feld als $1, $2, $N. Damit:

  • printf "%s ", $2; druckt das 2. Feld und ein Leerzeichen nach.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: Durchläuft die Felder 3 bis zum letzten Feld (NF ist die Anzahl der Felder) und druckt für jedes Feld das erste Feld, einen : und dann das aktuelle Feld und ein :1.
  • print "": Hiermit wird nur eine letzte neue Zeile gedruckt.

Oder 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 

Erläuterung

Der -a lässt Perl sich wie awk verhalten und teilt seine Eingabe in Leerzeichen auf. Hier werden die Felder im Array @F gespeichert, was bedeutet, dass das erste Feld $F[0], das zweite $F[1] usw. ist. Also:

  • print "$F[1] ": Druckt das 2. Feld.
  • print "$F[0]:$_:1 " for @F[2..$#F];: Iteriere über die Felder 3 bis zum letzten Feld ($#F ist die Anzahl der Elemente im Array @F, also nimmt @F[2..$#F] einen Array-Slice ab dem 3. Element bis das Ende des Arrays) und drucken Sie das 1. Feld, ein :, dann das aktuelle Feld und ein :1.
  • print "\n": Hiermit wird nur eine letzte neue Zeile gedruckt.
22
terdon

Hier ist ein schrecklich sed Weg!

$ 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

Besser lesbar:

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/ $//'

Anmerkungen

  • -r benutze ERE
  • s/old/new/ ersetze old durch new
  • ^([0-9]+) Speichern Sie einige Nummern am Zeilenanfang
  • \1 Rückverweis auf das erste gespeicherte Muster
  • :a beschriften Sie diesen Abschnitt des Skripts a
  • ( |$) entweder ein Leerzeichen oder das Ende der Zeile
  • t Testen Sie, ob der letzte Austausch erfolgreich war. Wenn ja, führen Sie den nächsten Befehl aus
  • a Finde das Label :a und mache es erneut
  • s/ $// Entfernen Sie das nachfolgende Leerzeichen

Nachdem wir also die Struktur zum ersten Teil hinzugefügt haben, finden wir wiederholt die letzte Instanz der Struktur und wenden sie auf die nächste Nummer an ...

Aber ich stimme zu, dass andere Tools es einfacher machen ...

12
Zanna

Mit awk:

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

oder mit 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

Ausgabe:

 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
Cyrus

Nun, Sie können es in sed tun, aber python funktioniert auch.

$ ./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

Der Inhalt des reformatfile.py ist wie folgt:

#!/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))

Wie funktioniert das? Es ist wirklich nichts Besonderes los. Wir öffnen das erste Befehlszeilenargument als Datei zum Lesen und unterteilen jede Zeile in "Wörter" oder einzelne Elemente. Das erste Wort wird zur Variable pref, und wir drucken auf das zweite (words [1]) -Element, das mit einem Leerzeichen endet. Als nächstes konstruieren wir eine neue Menge von "Wörtern" über Listenverständnisse und die Funktion .join() auf einer temporären Liste von pref, jedem Wort und String "1". Der letzte Schritt besteht darin, diese auszudrucken

5

Mit 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

Es geht darum, durch Leerzeichen getrennte Felder im gewünschten Format zu formatieren:

  • printf("%s ", $2) druckt das zweite Feld mit einem Leerzeichen nach

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) durchläuft das drittletzte bis vorletzte Feld und druckt die Felder in dem gewünschten Format (erstes Feld, dann ein Doppelpunkt, dann das aktuelle Feld, dann ein Doppelpunkt, schließlich 1) mit einem nachgestellten Leerzeichen

  • printf("%s:%s:1\n", $1, $NF) druckt das letzte Feld mit Zeilenumbruch

Beispiel:

% 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
4
heemayl