Textbearbeitung mit sed

12

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, die oben genannten mit sed zu tun? Oder muss ich auf Python zurückgreifen?

Zanna
quelle

Antworten:

22

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

$ 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 wird jede Zeile von Eingang auf Leerzeichen geteilt (Standardeinstellung), von denen jede Feldern wie Speicher $1, $2, $N. So:

  • 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 (entspricht NFder Anzahl der Felder) und druckt für jedes Feld das erste Feld, ein :, dann das aktuelle Feld und ein :1.
  • print "" : dies gibt nur eine letzte neue Zeile aus.

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

Die -aMarken perlverhalten sich wie awkund teilen ihre Eingabe in Leerzeichen auf. Hier werden die Felder im Array gespeichert @F, dh das 1. Feld $F[0]ist das 2. $F[1]usw. Also:

  • print "$F[1] " : druckt das 2. Feld aus.
  • print "$F[0]:$_:1 " for @F[2..$#F];: Iteriere über die Felder 3 bis zum letzten Feld ( $#Fist die Anzahl der Elemente im Array @F, also @F[2..$#F]nimmt ein Array Slice ab dem 3. Element bis zum Ende des Arrays) und drucke das 1. Feld, a :, dann das aktuelle Feld und a :1.
  • print "\n" : dies gibt nur eine letzte neue Zeile aus.
terdon
quelle
12

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/ersetzen oldmitnew
  • ^([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 die letzte Ersetzung erfolgreich war. Wenn ja, führen Sie den nächsten Befehl aus
  • aFinde das Etikett :aund mache es noch einmal
  • s/ $// Entfernen Sie das nachfolgende Leerzeichen

Nachdem wir 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 ...

Zanna
quelle
Ich habe auf Ihre Lösung gewartet: D
Ravexina
: D Es hat eine Weile gedauert, @Ravexina - Ich glaube, Muru kann eine sauberere machen
Zanna
5

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 
Cyrus
quelle
5

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

Inhalte der reformatfile.pysind 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 teilen jede Zeile in "Wörter" oder einzelne Elemente auf. Erste Worte werdenpref variabel, 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 .join()funktionieren auf einer temporären Liste von Präferenzen, jedem Wort und jeder Zeichenkette "1". Der letzte Schritt besteht darin, diese auszudrucken

Sergiy Kolodyazhnyy
quelle
4

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) Iteriert über das drittletzte bis vorletzte Feld und druckt die Felder im gewünschten Format (erstes Feld, dann ein Doppelpunkt, dann das aktuelle Feld, dann ein Doppelpunkt, schließlich 1) mit einem Leerzeichen nach

  • 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
heemayl
quelle