Wie teile ich eine begrenzte Zeichenfolge in awk in ein Array auf?


169

So teilen Sie die Zeichenfolge, wenn sie Pipe-Symbole enthält |. Ich möchte sie teilen, um im Array zu sein.

Ich habe es versucht

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Welches funktioniert gut. Wenn meine Zeichenfolge wie folgt "12|23|11"ist, wie teile ich sie dann in ein Array auf?


3
Beachten Sie, dass Ihre Ausgabe die Array-Elemente ohne Trennzeichen verkettet. Wenn Sie stattdessen möchten, dass sie durch getrennt werden OFS, setzen Sie Kommas dazwischen, damit printsie als separate Argumente angezeigt werden.
Dubiousjim

Oder Sie können sed verwenden:echo "12:23:11" | sed "s/.*://"
matschig

@slushy: Dein Befehl ist überhaupt nicht das, was der Fragesteller braucht. Ihr Befehl ( echo "12:23:11" | sed "s/.*://") löscht alles bis (und einschließlich) dem letzten ":", wobei nur die "11" beibehalten wird ... es funktioniert, um die letzte Nummer zu erhalten, müsste aber geändert werden (auf schwer lesbare Weise), um zu erhalten Die 2. Nummer usw. awk (und awks Split) ist viel eleganter und lesbarer.
Olivier Dulac

Wenn Sie auf ein einzelnes Zeichen teilen müssen, können Sie verwendencut
ccpizza

Antworten:


273

Hast du es versucht:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh, wenn Sie unter Solaris arbeiten, müssen Sie angesichts der Zeichenfolgenlänge / usr / xpg4 / bin / awk verwenden .
Dimitre Radoulov

5
"arbeitet nicht für mich". vor allem mit Doppelpunkten zwischen den Echowerten und der Aufteilung, die so eingestellt sind, dass sie auf '|' aufgeteilt werden ??? Tippfehler? Viel Glück für jeden.
Shellter

1
Besser mit einer Erklärung der Syntax.
Alston

2
Dies funktioniert in GNU awk nicht, da das dritte Argument für splitregulärer Ausdruck und |ein spezielles Symbol ist, das maskiert werden muss. Verwenden Siesplit($0, a, "\|")
WhiteWind

1
@WhiteWind: Eine andere Möglichkeit, um sicherzustellen, dass |dies als Zeichen und nicht als spezielles Symbol angesehen wird, besteht darin, es zwischen Folgendes zu setzen []: dh split($0, a, "[|]") # Ich mag dies in einigen Fällen besser als '\ |', insbesondere als eine Variante von Regexp ( perl vs grep vs .. andere?) können "|" haben wörtlich interpretiert und "\ |" gesehen als Regex-Separator, anstelle des Gegenteils ... ymmv
Olivier Dulac

119

Um einen String in ein Array zu teilen, verwenden awkwir die Funktion split():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

Wenn kein Trennzeichen angegeben wird, wird das verwendet FS, das standardmäßig das Leerzeichen verwendet:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Wir können ein Trennzeichen geben, zum Beispiel ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Was gleichbedeutend ist mit dem Einstellen durch FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

In gawk können Sie das Trennzeichen auch als regulären Ausdruck bereitstellen:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

Und sehen Sie sogar, was der Begrenzer bei jedem Schritt war, indem Sie seinen vierten Parameter verwenden:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Zitieren wir die Manpage von GNU awk :

split (string, array [, fieldsep [, seps]])

Teilen Sie die Schnur in durch getrennte Teile Feldsep Teile und speichern Sie die Teile im Array und die Trennzeichenfolgen im Seps- Array. Das erste Stück wird in gespeichert array[1], das zweite Stück in array[2]und so weiter. Der Zeichenfolgenwert des dritten Arguments, fieldsep , ist ein regulärer Ausdruck, der beschreibt, wo die Zeichenfolge aufgeteilt werden soll (ähnlich wie FS ein regulärer Ausdruck sein kann, der beschreibt, wo Eingabedatensätze aufgeteilt werden sollen). Wenn fieldsep weggelassen wird, wird der Wert von FS verwendet. split()Gibt die Anzahl der erstellten Elemente zurück. seps ist eine gawkErweiterung, seps[i]zwischen der sich die Trennzeichenfolge befindetarray[i]und array[i+1]. Wenn fieldsep nachfolgende eingegebenist ein einzelnes Leerzeichen, dann wird jedes führende Leerzeichen seps[0]und jedes nachfolgende Leerzeichen eingegeben seps[n], wobei n der Rückgabewert von split()(dh die Anzahl der Elemente im Array) ist.


Erwähnen Sie nur, dass Sie gnu awk verwenden, nicht reguläres awk (das keine Trennzeichen in seps [] speichert und andere Einschränkungen aufweist)
Olivier Dulac

17

Bitte sei spezifischer! Was meinst du mit "es funktioniert nicht"? Veröffentlichen Sie die genaue Ausgabe (oder Fehlermeldung), Ihr Betriebssystem und Ihre awk-Version:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

Oder mit split:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Bearbeiten: Unter Solaris müssen Sie POSIX awk ( / usr / xpg4 / bin / awk ) verwenden, um 4000 Felder korrekt zu verarbeiten.


for(i = 0oder for(i = 1?
PiotrNycz

i = 0, weil ich ++ i after benutze (nicht i ++).
Dimitre Radoulov

3
Ok - das habe ich nicht bemerkt. Ich glaube fest daran, dass besser lesbar wäre for (i = 1; i <= n; ++i)...
PiotrNycz

5

Ich mag die echo "..." | awk ...Lösung nicht, da sie unnötig forkund unnötig istexec Systemaufrufe aufruft.

Ich bevorzuge eine Dimiter-Lösung mit einer kleinen Wendung

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

Oder eine etwas kürzere Version:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

In diesem Fall wird der Ausgabedatensatz zusammengestellt, was eine echte Bedingung ist, sodass er gedruckt wird.

In diesem speziellen Fall ist die stdin Umleitung durch das Einstellen von erspart werden interne Variable:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

ich benutzte eine ganze Weile, aber in Dies könnte durch interne String-Manipulation verwaltet werden. Im ersten Fall wird die ursprüngliche Zeichenfolge durch den internen Terminator aufgeteilt. Im zweiten Fall wird angenommen, dass die Zeichenfolge immer Ziffernpaare enthält, die durch ein Ein-Zeichen-Trennzeichen getrennt sind.

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

Das Ergebnis ist in allen Fällen

112312

Ich denke, das Endergebnis sollten die Referenzen der awk-Array-Variablen sein, unabhängig vom angegebenen Beispiel für die Druckausgabe. Aber Sie haben einen wirklich einfachen Bash-Fall verpasst, um Ihr Endergebnis zu erzielen. T = '12: 23: 11 '; Echo $ {T //:}
Daniel Liston

@ DanielListon Du hast recht! Vielen Dank! Ich wusste nicht, dass das Trailing / in diesem bashAusdruck
belassen werden

4

Hat tatsächlich awkeine Funktion namens 'Input Field Separator Variable' Link . So wird es benutzt. Es ist nicht wirklich ein Array, aber es verwendet die internen $ -Variablen. Zum Teilen einer einfachen Zeichenfolge ist es einfacher.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

sollte arbeiten.



1

Scherz? :) :)

Wie wäre es mit echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Dies ist meine Ausgabe:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

also ich denke es funktioniert doch ..


liegt das an der länge der string? seitdem ist meine Saitenlänge 4000.
Irgendwelche

1

Ich weiß, dass dies eine alte Frage ist, aber ich dachte, vielleicht jemand wie mein Trick. Zumal diese Lösung nicht auf eine bestimmte Anzahl von Elementen beschränkt ist.

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

Die Ausgabe wird sein:

Item: 12
Item: 23
Item: 11
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.