มีฟิลด์ที่เก็บ FS ตัวคั่นฟิลด์ที่แน่นอนที่ใช้เมื่ออยู่ในนิพจน์ทั่วไปซึ่งเทียบเท่ากับ RT สำหรับ RS หรือไม่

Jan 04 2021

ใน4.1.2 Record Splittinggawkของ GNU Awkเราสามารถอ่าน:

เมื่อRSใดที่เป็นอักขระเดี่ยวRTประกอบด้วยอักขระเดี่ยวเดียวกัน อย่างไรก็ตามเมื่อใดที่RSเป็นนิพจน์ทั่วไปจะRTมีข้อความอินพุตจริงที่ตรงกับนิพจน์ทั่วไป

ตัวแปรนี้จะRTเป็นประโยชน์อย่างมากในบางกรณี

ในทำนองเดียวกันเราสามารถตั้งค่านิพจน์ทั่วไปเป็นตัวคั่นฟิลด์ ตัวอย่างเช่นในที่นี้เราอนุญาตให้เป็น ";" หรือ "|":

$ 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

อย่างไรก็ตามหากเราต้องการแพ็คข้อมูลอีกครั้งเราไม่มีทางรู้ว่าตัวคั่นใดปรากฏขึ้นระหว่างสองฟิลด์ ดังนั้นหากในตัวอย่างก่อนหน้านี้ฉันต้องการวนซ้ำช่องและพิมพ์พร้อมกันอีกครั้งโดยใช้FSมันจะพิมพ์นิพจน์ทั้งหมดในทุกกรณี:

$ 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 อนุญาตให้ทำหรือไม่

(ตัวอย่างที่ให้ไว้ในคำถามค่อนข้างง่าย แต่เพื่อแสดงประเด็น)

คำตอบ

8 anubhava Jan 04 2021 at 16:34

มีวิธี "บรรจุใหม่" ช่องโดยใช้ตัวคั่นช่องเฉพาะที่ใช้แยกแต่ละช่องหรือไม่

การใช้gnu-awk split()ที่มีพารามิเตอร์ที่ 4 พิเศษสำหรับตัวคั่นที่ตรงกันโดยใช้ regex ที่ให้มา:

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

เวอร์ชันที่อ่านได้มากขึ้น:

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"

จด 4 sepsพารามิเตอร์ในร้านค้าที่อาร์เรย์ของข้อความที่ตรงกันโดยแสดงออกปกติเช่นใช้ในพารามิเตอร์ที่split 3/[;|]/

แน่นอนมันไม่เป็นที่สั้นและง่ายเหมือนRS, ORSและRTซึ่งสามารถเขียนเป็น:

awk -v RS='[;|]' '{ORS = RT} 1' <<< "$s"
5 EdMorton Jan 04 2021 at 22:41

ในฐานะที่เป็น@anubhava กล่าวถึง , เพ่งพิศมีsplit()(และpatsplit()ซึ่งจะFPATเป็นsplit()คือการFS- ดูhttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) เพื่อทำสิ่งที่คุณต้องการ หากคุณต้องการฟังก์ชันเดียวกันกับ POSIX awk แล้ว:

$ 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)
    }
}

สังเกตการจัดการเฉพาะด้านบนของกรณีที่ตัวคั่นฟิลด์เป็น" "เพราะนั่นหมายถึง 2 สิ่งที่แตกต่างจากค่าตัวคั่นฟิลด์อื่น ๆ ทั้งหมด:

  1. ฟิลด์ถูกคั่นด้วยโซ่ของพื้นที่สีขาวใด ๆ และ
  2. ช่องว่างนำหน้าจะถูกละเว้นเมื่อเติม $ 1 (หรือ flds [1] ในกรณีนี้) ดังนั้นหากมีอยู่จะต้องจับพื้นที่สีขาวใน seps [0] `สำหรับวัตถุประสงค์ของเราเนื่องจากทุก seps [N] มีความสัมพันธ์กัน ด้วย flds [N] ที่นำหน้า

ตัวอย่างเช่นการเรียกใช้ข้อมูลข้างต้นในไฟล์อินพุต 3 ไฟล์เหล่านี้:

$ head file{1..3}
==> file1 <==
hello;how|are you

==> file2 <==
hello how are_you

==> file3 <==
    hello how are_you

เราจะได้ผลลัพธ์ต่อไปนี้ซึ่งแต่ละฟิลด์จะแสดงเป็นหมายเลขฟิลด์จากนั้นค่าฟิลด์ภายใน[...]ตัวคั่นภายใน<...>ทั้งหมดภายใน{...}(โปรดทราบว่าseps[0]มีการเติม IFF FS คือ" "และเร็กคอร์ดเริ่มต้นด้วยช่องว่าง):

$ 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]<>}
3 RamanSailopal Jan 04 2021 at 16:51

ตัวเลือกอื่นในการแบ่งคือใช้การจับคู่เพื่อค้นหาตัวคั่นฟิลด์และอ่านลงในอาร์เรย์:

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"