Gibt es ein Feld, in dem das genaue Feldtrennzeichen FS gespeichert ist, das in einem regulären Ausdruck verwendet wird, das RT für RS entspricht?
In GNU Awks 4.1.2 Record Splitting mit könnengawk wir lesen:
Wann
RS
ist ein einzelnes Zeichen,RT
enthält das gleiche einzelne Zeichen. WennRS
es sich jedoch um einen regulären Ausdruck handelt,RT
enthält er den tatsächlichen Eingabetext, der mit dem regulären Ausdruck übereinstimmt.
Diese Variable RT
ist in einigen Fällen sehr nützlich .
Ebenso können wir einen regulären Ausdruck als Feldtrennzeichen festlegen. Zum Beispiel lassen wir hier zu, dass es entweder ";" oder "|":
$ gawk -F';' '{print NF}' <<< "hello;how|are you" 2 # there are 2 fields, since ";" appears once $ gawk -F'[;|]' '{print NF}' <<< "hello;how|are you"
3 # there are 3 fields, since ";" appears once and "|" also once
Wenn wir die Daten jedoch erneut packen möchten, können wir nicht feststellen, welches Trennzeichen zwischen zwei Feldern angezeigt wurde. Wenn ich also im vorherigen Beispiel die Felder durchlaufen und sie mithilfe von erneut zusammen drucken möchte, wird FS
in jedem Fall der gesamte Ausdruck gedruckt :
$ gawk -F'[;|]' '{for (i=1;i<=NF;i++) printf ("%s%s", $i, FS)}' <<< "hello;how|are you"
hello[;|]how[;|]are you[;|] # a literal "[;|]" shows in the place of FS
Gibt es eine Möglichkeit, die Felder mit dem speziellen Feldtrennzeichen, das zum Aufteilen der einzelnen Felder verwendet wird, neu zu verpacken, ähnlich wie dies bei RT möglich wäre?
(Die Beispiele in der Frage sind ziemlich einfach, aber nur um den Punkt zu zeigen)
Antworten
Gibt es eine Möglichkeit, die Felder mit dem speziellen Feldtrennzeichen, das zum Teilen der einzelnen Felder verwendet wird, neu zu verpacken?
Wenn Sie dies verwenden gnu-awk
split(), erhalten Sie einen zusätzlichen 4. Parameter für das übereinstimmende Trennzeichen unter Verwendung des mitgelieferten regulären Ausdrucks:
s="hello;how|are you"
awk 'split($0, flds, /[;|]/, seps) {for (i=1; i in seps; i++) printf "%s%s", flds[i], seps[i]; print flds[i]}' <<< "$s"
hello;how|are you
Eine besser lesbare Version:
s="hello;how|are you"
awk 'split($0, flds, /[;|]/, seps) { for (i=1; i in seps; i++) printf "%s%s", flds[i], seps[i] print flds[i] }' <<< "$s"
Beachten Sie den 4. seps
Parameter, in split
dem ein Array von übereinstimmendem Text durch reguläre Ausdrücke gespeichert wird, die im 3. Parameter verwendet werden, d /[;|]/
. H.
Natürlich ist es nicht so kurz und einfach wie RS
, ORS
und RT
, die wie folgt geschrieben werden kann:
awk -v RS='[;|]' '{ORS = RT} 1' <<< "$s"
Wie @anubhava erwähnt , hat gawk split()
(und patsplit()
das ist so FPAT
wie es split()
ist FS
- zu sehenhttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) um zu tun was du willst. Wenn Sie die gleiche Funktionalität mit einem POSIX awk wünschen, dann:
$ cat tst.awk function getFldsSeps(str,flds,fs,seps, nf) { delete flds delete seps str = $0
if ( fs == " " ) {
fs = "[[:space:]]+"
if ( match(str,"^"fs) ) {
seps[0] = substr(str,RSTART,RLENGTH)
str = substr(str,RSTART+RLENGTH)
}
}
while ( match(str,fs) ) {
flds[++nf] = substr(str,1,RSTART-1)
seps[nf] = substr(str,RSTART,RLENGTH)
str = substr(str,RSTART+RLENGTH)
}
if ( str != "" ) {
flds[++nf] = str
}
return nf
}
{
print
nf = getFldsSeps($0,flds,FS,seps)
for (i=0; i<=nf; i++) {
printf "{%d:[%s]<%s>}%s", i, flds[i], seps[i], (i<nf ? "" : ORS)
}
}
Beachten Sie die oben beschriebene spezifische Behandlung des Falles, in dem sich das Feldtrennzeichen befindet, " "
da dies zwei Dinge bedeutet, die sich von allen anderen Feldtrennerwerten unterscheiden:
- Felder sind tatsächlich durch Ketten eines beliebigen Leerraums getrennt, und
- Führender Leerraum ist beim Auffüllen von $ 1 (oder in diesem Fall flds [1]) zu ignorieren, und dieser Leerraum muss, falls vorhanden, für unsere Zwecke in seps [0] `erfasst werden, da jeder seps [N] zugeordnet ist mit den flds [N], die davor stehen.
Führen Sie beispielsweise die folgenden Schritte für diese 3 Eingabedateien aus:
$ head file{1..3}
==> file1 <==
hello;how|are you
==> file2 <==
hello how are_you
==> file3 <==
hello how are_you
Wir würden die folgende Ausgabe erhalten, in der jedes Feld als Feldnummer angezeigt wird, [...]
dann der Feldwert innerhalb <...>
und dann das Trennzeichen innerhalb (alles innerhalb {...}
, wenn seps[0]
IFF der FS ist " "
und der Datensatz mit Leerzeichen beginnt):
$ awk -F'[,|]' -f tst.awk file1
hello;how|are you
{0:[]<>}{1:[hello;how]<|>}{2:[are you]<>}
$ awk -f tst.awk file2 hello how are_you {0:[]<>}{1:[hello]< >}{2:[how]< >}{3:[are_you]<>} $ awk -f tst.awk file3
hello how are_you
{0:[]< >}{1:[hello]< >}{2:[how]< >}{3:[are_you]<>}
Eine alternative Option zum Teilen besteht darin, die Feldtrennzeichen mithilfe von match zu finden und in ein Array einzulesen:
awk -F'[;|]' '{
str=$0; # Set str to the line while (match(str,FS)) { # Loop through rach match of the field separator map[cnt+=1]=substr(str,RSTART,RLENGTH); # Create an array of the field separators str=substr(str,RSTART+RLENGTH) # Set str to the rest of the string after the match string } for (i=1;i<=NF;i++) { printf "%s%s",$i,map[i] # Loop through each record, printing it along with the field separator held in the array map.
}
printf "\n"
}' <<< "hello;how|are you"