Apakah ada bidang yang menyimpan pemisah bidang FS yang digunakan saat dalam ekspresi reguler, setara dengan RT untuk RS?
Dalam Pemisahan Rekaman 4.1.2 GNU Awk dengangawk kita dapat membaca:
Ketika
RS
satu karakter,RT
berisi karakter tunggal yang sama. Namun, bilaRS
merupakan ekspresi reguler,RT
berisi teks masukan sebenarnya yang cocok dengan ekspresi reguler.
Variabel RT
ini sangat berguna dalam beberapa kasus .
Demikian pula, kita dapat menyetel ekspresi reguler sebagai pemisah bidang. Misalnya, di sini kami mengizinkannya menjadi ";" atau "|":
$ 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
Namun, jika kami ingin mengemas data lagi, kami tidak memiliki cara untuk mengetahui pemisah mana yang muncul di antara dua bidang. Jadi jika dalam contoh sebelumnya saya ingin mengulang melalui bidang dan mencetaknya bersama lagi dengan menggunakan FS
, ini mencetak seluruh ekspresi dalam setiap kasus:
$ 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
Apakah ada cara untuk "mengemas kembali" bidang menggunakan pemisah bidang tertentu yang digunakan untuk memisahkan masing-masing, serupa dengan yang dapat dilakukan RT?
(contoh yang diberikan dalam pertanyaan agak sederhana, tetapi hanya untuk menunjukkan maksudnya)
Jawaban
Apakah ada cara untuk "mengemas kembali" bidang menggunakan pemisah bidang tertentu yang digunakan untuk memisahkan masing-masing
Penggunaan gnu-awk
split()yang memiliki parameter ke-4 ekstra untuk pembatas yang cocok menggunakan regex yang disediakan:
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
Versi yang lebih mudah dibaca:
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"
Catat seps
parameter ke-4 split
yang menyimpan larik teks yang cocok dengan ekspresi reguler yang digunakan dalam parameter ke-3 yaitu /[;|]/
.
Tentu saja tidak sesingkat & sesederhana itu RS
, ORS
dan RT
, yang bisa dituliskan sebagai:
awk -v RS='[;|]' '{ORS = RT} 1' <<< "$s"
Sebagai @anubhava menyebutkan , melongo memiliki split()
(dan patsplit()
yang untuk FPAT
sebagai split()
adalah FS
- lihathttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) untuk melakukan apa yang Anda inginkan. Jika Anda menginginkan fungsionalitas yang sama dengan POSIX awk maka:
$ 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)
}
}
Perhatikan penanganan khusus di atas dari kasus di mana pemisah bidang " "
karena itu berarti 2 hal berbeda dari semua nilai pemisah bidang lainnya:
- Bidang sebenarnya dipisahkan oleh rantai spasi putih, dan
- Ruang kosong utama harus diabaikan ketika mengisi $ 1 (atau flds [1] dalam kasus ini) dan sehingga ruang putih, jika ada, harus diambil dalam seps [0] `untuk tujuan kita karena setiap seps [N] dikaitkan dengan flds [N] yang mendahuluinya.
Misalnya, menjalankan di atas pada 3 file masukan ini:
$ head file{1..3}
==> file1 <==
hello;how|are you
==> file2 <==
hello how are_you
==> file3 <==
hello how are_you
kita akan mendapatkan output berikut di mana setiap bidang ditampilkan sebagai nomor bidang lalu nilai bidang di dalamnya [...]
kemudian pemisah di dalamnya <...>
, semua di dalamnya {...}
(perhatikan bahwa seps[0]
IFF diisi dengan FS " "
dan catatan dimulai dengan spasi):
$ 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]<>}
Opsi alternatif untuk memisahkan adalah menggunakan kecocokan untuk menemukan pemisah bidang dan membacanya menjadi sebuah larik:
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"