Wie lösche ich ein Eingabefeld in AWK?

7

Ich transformiere einige Daten mit awk(oder gawk) und möchte eines der Eingabefelder löschen, bevor ich die Ausgabe erneut drucke.

Was ich erreichen möchte, ist Folgendes:

~ $ echo 'field1,field2,field3' | awk -F, '{transform($1); delete($2); print $0;}'
new_field1,field3

Ich kann nicht einfach eine leere Zeichenfolge zuweisen, $2da dies zu new_field1,,field3(beachten Sie die beiden Kommas) führt.

Ich könnte explizit nur die Felder drucken, die ich möchte, aber das ist nicht sehr elegant, da ich weit mehr Felder als 3 habe und am Ende optionale Felder stehen (hier nicht gezeigt). Deshalb bevorzuge ich print $0. Ich muss nur zuerst einige Felder entfernen.

Irgendeine Idee?

MLu
quelle

Antworten:

7

Das Löschen von Feldern in awk ist bekanntermaßen schwierig. Es scheint eine so einfache (und oft erforderliche) Operation zu sein, aber es ist schwieriger als es sein sollte.

Siehe Gibt es eine Möglichkeit, Felder in awk vollständig zu löschen, damit keine zusätzlichen Trennzeichen gedruckt werden? von Stack Overflow für einen guten Weg, dies zu tun.

Ich habe die rmcol()Funktion in @ ghotis Antwort kopiert , so dass wir hier auf U & L eine Kopie haben:

function rmcol(col,     i) {
  for (i=col; i<NF; i++) {
    $i=$(i+1)
  }
  NF--
}

Es löscht die angegebene Spalte aus der aktuellen Eingabezeile und dekrementiert den Feldzähler ( NF) entsprechend.

Ich habe keine Ahnung, was Ihre transform()Funktion tut, daher werde ich nicht einmal versuchen, dies zu duplizieren - aber hier ist ein Beispiel für die Verwendung rmcol()in einem awkEinzeiler:

$ echo 'field1,field2,field3' | awk -F, -v OFS=, '
  function rmcol(col,     i) {
    for (i=col; i<NF; i++) {
      $i=$(i+1)
    }
    NF--
  }

  { rmcol(2); print; }
  '
field1,field3

Übrigens, wenn Sie mehrere Felder aus einer Eingabezeile löschen müssen, ist es am besten / einfachsten, sie in umgekehrter Reihenfolge zu löschen. Das heißt, löschen Sie die höchsten nummerierten Felder zuerst . Warum? Da die Felder mit der höheren Nummer jedes Mal neu nummeriert werden, wenn Sie ein Feld mit der niedrigeren Nummer löschen, ist es sehr schwierig zu verfolgen, welche Feldnummer zu welchem ​​Feld gehört.


Übrigens dient delete()in awkzum Löschen von Elementen eines Arrays - nicht zum Löschen von Feldern aus einer Eingabezeile. Sie könnten split()jede Eingabezeile (ein FS) in ein Array eingeben und das zweite Array-Element löschen, aber dann müssten Sie eine join()Funktion schreiben , um das Array mit einem Komma (oder OFS) zu drucken, das jedes Feld trennt.

Selbst dies wäre komplizierter als erwartet, da alle Arrays in awkassoziativen Arrays sind (dh nicht numerisch indiziert sind), sodass Array-Elemente 3+ delete(array[2]) nicht automatisch in Elemente 2+ verschoben werden. Sie müssten Ihre eigene Wrapper-Funktion herumschreiben, um delete()für Arrays fast dasselbe zu tun rmcol()wie für Eingabefelder.

cas
quelle
4

Einige Alternativen

1) Verarbeiten Sie die Eingabe vor, um das Feld zuerst zu entfernen. cutDies ist einfach, wenn das Feldtrennzeichen ein einzelnes Zeichen ist

$ s='field1,field2,field3'
$ # use 'cut -d, -f1,3-' if --complement option is not available
$ echo "$s" | cut -d, --complement -f2
field1,field3
$ echo "$s" | cut -d, --complement -f2 | awk 'BEGIN{FS=OFS=","} {$1="new"} 1'
new,field3

2) verwenden perl

$ # indexing starts from 0, the array @F contains the input fields
$ # $#F will give index of last element in the array
$ echo "$s" | perl -F, -lane '$F[0]="new"; print join ",", @F[0,2..$#F]'
new,field3
Sundeep
quelle