RS 용 RT와 동일한 정규식에서 사용되는 정확한 필드 구분 기호 FS를 저장하는 필드가 있습니까?

Jan 04 2021

에서 GNU awk는의 4.1.2 기록 분할로gawk 우리는 읽을 수 있습니다 :

RS하나의 문자, RT같은 하나의 문자가 포함되어 있습니다. 그러나 when 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 번째 매개 변수가 있습니다 :

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"

세 번째 seps매개 변수에서 split사용되는 정규 표현식에 의해 일치하는 텍스트의 배열을 저장하는 네 번째 매개 변수에 주의하십시오 /[;|]/.

물론 다음과 같이 작성할 수있는 RS, ORS및 만큼 짧고 간단하지는 않습니다 RT.

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

으로 @anubhava 언급 , GAWK가있다 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)
    }
}

필드 구분 기호가 " "다른 모든 필드 구분 기호 값과 다른 두 가지를 의미하기 때문에 위의 특정 처리에 유의하십시오 .

  1. 필드는 실제로 공백의 체인으로 구분됩니다.
  2. $ 1 (또는이 경우 flds [1])을 채울 때 선행 공백을 무시해야하며 공백이있는 경우 모든 seps [N]이 연관되어 있으므로 우리의 목적을 위해 seps [0]`에서 캡처해야합니다. 앞에 flds [N]이 있습니다.

예를 들어 다음 3 개의 입력 파일에서 위를 실행합니다.

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

==> file2 <==
hello how are_you

==> file3 <==
    hello how are_you

우리는 다음 각 필드는 다음 필드 번호 내의 필드의 값으로 표시됩니다 출력을 얻을 것 [...]내에서 다음 분리 <...>, 내의 모든 {...}(주 seps[0]FS IFF 채워입니다 " "및 공백으로 기록 시작) :

$ 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

분할하는 다른 옵션은 match를 사용하여 필드 구분 기호를 찾고 배열로 읽는 것입니다.

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"