Normal ifadede kullanılan, RS için RT'ye eşdeğer, tam alan ayırıcı FS'yi depolayan bir alan var mı?
In GNU awk en 4.1.2 Kayıt Yarma ilegawk biz okuyabilir:
RS
Tek bir karakter olduğunda ,RT
aynı tek karakteri içerir. Bununla birlikte,RS
normal bir ifade olduğunda , normal ifadeyleRT
eşleşen gerçek girdi metnini içerir.
Bu değişken bazı durumlardaRT
çok kullanışlıdır .
Benzer şekilde, alan ayırıcı olarak bir normal ifade belirleyebiliriz. Örneğin, burada biz onun ";" olmasına izin veriyoruz. veya "|":
$ 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
Bununla birlikte, verileri tekrar paketlemek istersek, iki alan arasında hangi ayırıcının göründüğünü bilmenin bir yolu yoktur. Dolayısıyla, önceki örnekte alanlar arasında döngü yapmak ve kullanarak bunları tekrar birlikte yazdırmak istersem FS
, her durumda tüm ifadeyi yazdırır:
$ 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
RT'nin yapmasına benzer şekilde, her birini bölmek için kullanılan belirli alan ayırıcısını kullanarak alanları "yeniden paketlemenin" bir yolu var mı?
(soruda verilen örnekler oldukça basittir, ancak sadece noktayı göstermek için)
Yanıtlar
Alanları, her birini bölmek için kullanılan belirli alan ayırıcısını kullanarak "yeniden paketlemenin" bir yolu var mı?
Kullanımı gnu-awk
split()o verilen regex kullanarak sınırlayıcı eşleşti için ekstra 4 parametresi vardır:
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
Daha okunaklı bir versiyon:
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"
3. parametrede, yani 3. parametrede kullanılan normal ifadeye göre eşleşen bir metin dizisi depolayan 4. seps
parametreyi not edin .split
/[;|]/
Tabii ki Kısa ve olarak basit gibi olmadığı RS
, ORS
ve RT
şu şekilde yazılabilir:
awk -v RS='[;|]' '{ORS = RT} 1' <<< "$s"
Olarak @anubhava söz , Gawk sahiptir split()
(ve patsplit()
için olan FPAT
şekilde split()
olduğunu FS
- Bkzhttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) istediğinizi yapmak için. Bir POSIX awk ile aynı işlevselliği istiyorsanız, o zaman:
$ 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)
}
}
Alan ayırıcısının, " "
diğer tüm alan ayırıcı değerlerinden 2 şey farklı olduğu anlamına geldiği durumda yukarıdaki özel işleme dikkat edin :
- Alanlar aslında herhangi bir beyaz boşluğun zincirleriyle ayrılır ve
- Baştaki beyaz boşluk, $ 1 (veya bu durumda flds [1]) doldurulurken göz ardı edilmelidir ve böylece beyaz boşluk, varsa, her seps [N] ilişkilendirildiğinden, amaçlarımız için seps [0] `içinde yakalanmalıdır. önündeki flds [N] ile.
Örneğin, yukarıdakileri bu 3 girdi dosyasında çalıştırmak:
$ head file{1..3}
==> file1 <==
hello;how|are you
==> file2 <==
hello how are_you
==> file3 <==
hello how are_you
Her alanın alan numarası olarak görüntülendiği aşağıdaki çıktıyı elde ederiz, ardından içindeki alan değeri ve [...]
ardından içindeki ayırıcı <...>
, hepsi içinde {...}
( seps[0]
IFF doldurulmuş olduğunu " "
ve kaydın beyaz boşlukla başladığını unutmayın ):
$ 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]<>}
Bölmek için alternatif bir seçenek, alan ayırıcılarını bulmak ve bunları bir dizide okumak için eşleştirmeyi kullanmaktır:
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"