AWK: 열이 시작되는 위치를 어떻게 알 수 있나요?

AWK: 열이 시작되는 위치를 어떻게 알 수 있나요?

입력 행을 구문 분석한 후 awk는 원래 행( $0)과 각 개별 열( $1, $2, ...)에 대한 액세스를 제공합니다. 이 프로세스를 수행하는 동안(요청 시 느리게) 두 번째 열의 문자 위치가 시작되는 위치를 정확히 알고 있습니다.

  1. 이 정보에 대한 액세스를 제공합니까(예: 원래 행 $0에서 두 번째 열이 시작되는 위치)?
  2. 그렇지 않다면 올바르게 찾을 수 있는 건전하고 우아한 방법이 있습니까? (동적 정규식 사용 FS, 특수 사례 처리 , 캡처 그룹 사용 등을 통해 awk의 내부 동작을 에뮬레이션하는 추악하고 비효율적인 방법을 작성하려고 합니다. FS==" "하지만 더 자세히 알아보기 전에 조언이 필요합니다.)

예 1(기본 FS):

$ echo -n -e " \tFirst \t\t  Second \t Third  \t"\
|awk -F" " '{print "FS:["FS"]";for(i=0;i<=5;i++)if(""!=$i)print "$"i":["$i"]"}'\
|sed 's/\t/\\t/g'

FS:[ ]
$0:[ \tFirst \t\t  Second \t Third  \t]
$1:[First]
$2:[Second]
$3:[Third]

Second여기 - 두 번째 열( )이 문자 로 시작한다는 것을 알아야 합니다 S.13번입력 줄의 문자(그래서 First키로 저장하고 Second \t Third \t나중에 사용하기 위해 전체 값을 값으로 유지/저장할 수 있음)


예 2(TAB을 FS로 사용):

$ echo -n -e " \tFirst \t\t  Second \t Third  \t"\
|awk -F"\t" '{print "FS:["FS"]";for(i=0;i<=5;i++)if(""!=$i)print "$"i":["$i"]"}'\
|sed 's/\t/\\t/g'

FS:[\t]
$0:[ \tFirst \t\t  Second \t Third  \t]
$1:[ ]
$2:[First ]
$4:[  Second ]
$5:[ Third  ]

First여기 - 두 번째 열( )이 문자 로 시작한다는 것을 알아야 합니다 F.세 번째 장소입력 줄의 문자 - (공백)을 키로 저장하고 First \t\t Second \t Third \t나중에 사용할 수 있도록 값으로 그대로 유지/저장할 수 있습니다.


예시 3(맞춤형 FS):

$ echo -n -e " \tFirst \t\t  Second \t Third  \t"\
|awk -F"[ \t]+" '{print "FS:["FS"]";for(i=0;i<=5;i++)if(""!=$i)print "$"i":["$i"]"}'\
|sed 's/\t/\\t/g'

FS:[[ \t]+]
$0:[ \tFirst \t\t  Second \t Third  \t]
$2:[First]
$3:[Second]
$4:[Third]

First여기 - 두 번째 열( )이 문자 로 시작한다는 것을 알아야 합니다 F.세 번째 장소행에 문자를 입력하십시오. 그러면 첫 번째 열이 빈 문자열임을 알고 First \t\t Second \t Third \t나중에 사용할 수 있도록 값으로 저장합니다.


예 4(복합 FS):

$ echo "-11...22;,;..;33-44...;"\
|awk -F"[^0-9-]+" '{print "FS:["FS"]";for(i=0;i<=5;i++)if(""!=$i)print "$"i":["$i"]"}'

FS:[[^0-9-]+]
$0:[-11...22;,;..;33-44...;]
$1:[-11]
$2:[22]
$3:[33-44]

22여기 - 두 번째 열( ) 이 문자로 시작한다는 것을 알아야 합니다 2.7번줄에 문자를 입력하세요. 이렇게 하면 나중에 사용할 수 있도록 -11키와 값으로 저장할 수 있습니다.22;,;..;33-44...;


기본적으로 아이디어는 사용자 지정 사용을 위해 일부 (첫 번째) 열을 가져오고 행의 나머지 부분(두 번째 열부터 행 끝까지)을 그대로 유지(변수에 저장)하는 것입니다.

답변1

Split()의 네 번째 인수로 GNU awk를 사용합니다.

$ cat tst.awk
{
    split($0,flds,FS,seps)
    key = flds[1]
    pos = length(seps[0] flds[1] seps[1]) + 1
    val = substr($0,pos)
    printf "key=<%s>\npos=<%s>\nval=<%s>\n\n", key, pos, val
}

$ printf -- ' \tFirst \t\t  Second \t Third  \t\n' | awk -f tst.awk
key=<First>
pos=<13>
val=<Second      Third          >

$ printf -- '-11...22;,;..;33-44...;\n' | awk -F'[^0-9-]+' -f tst.awk
key=<-11>
pos=<7>
val=<22;,;..;33-44...;>

답변2

GNU/awk에서는 split()선택적 seps인수를 사용한 다음 array각 필드에 대한 합계 배열을 반복 하고 seps구축하여 각 필드와 구분 기호의 길이를 누적할 수 있습니다.startsize

이 코드는 GNU/awk용입니다. Offsets() 함수는 텍스트 문자열과 필드 구분 기호 패턴을 받아들이고 시작 열과 필드 길이를 포함하는 배열 쌍을 반환합니다.

$ cat myCols
#! /bin/bash

myCols () {

    local Awk='
BEGIN { cmdDu = "od -A n -t a"; }
#.. Debug the input.
function Debug (tx, Local ) {
    printf ("\nLine %2d: %s\n", NR, tx);
    printf ("%s", tx) | cmdDu; close (cmdDu);
}
#.. Return arrays of column start and length.
function Offsets (col, lth, tx, re, Local, fld, sep, f) {
    delete col; delete lth;
    split (tx, fld, re, sep);
    c = length (sep[0]);
    for (f = 1; f in fld; ++f) {
        col[f] = 1 + c; lth[f] = length (fld[f]);
        c += length (fld[f]) + length (sep[f]);
    }
}
#.. Find fields and show the results.
function Fields (tx, re, Local, col, lth, f) {
    Offsets( col, lth, tx, re);
    for (f = 1; f in col; ++f) {
        printf ("Field %d col %3d lth %3d >%s<\n",
            f, col[f], lth[f], substr (tx, col[f], lth [f]));
    }
}
{ Debug( $0); }
NR == 1 { Fields( $0, ",[[:space:]]*"); }
NR == 2 { Fields( $0, FS); }
'
    awk -f <( printf '%s' "${Awk}" )

}

    {
        echo "Never,   Ever,  Forget,  but maybe,   Forgive"
        echo -n -e " \tFirst \t\t  Second \t Third  \t"
    } | myCols

테스트해 보세요.

$ ./myCols

Line  1: Never,   Ever,  Forget,  but maybe,   Forgive
   N   e   v   e   r   ,  sp  sp  sp   E   v   e   r   ,  sp  sp
   F   o   r   g   e   t   ,  sp  sp   b   u   t  sp   m   a   y
   b   e   ,  sp  sp  sp   F   o   r   g   i   v   e
Field 1 col   1 lth   5 >Never<
Field 2 col  10 lth   4 >Ever<
Field 3 col  17 lth   6 >Forget<
Field 4 col  26 lth   9 >but maybe<
Field 5 col  39 lth   7 >Forgive<

Line  2:    First         Second     Third      
  sp  ht   F   i   r   s   t  sp  ht  ht  sp  sp   S   e   c   o
   n   d  sp  ht  sp   T   h   i   r   d  sp  sp  ht
Field 1 col   3 lth   5 >First<
Field 2 col  13 lth   6 >Second<
Field 3 col  22 lth   5 >Third<
$ 

예를 들어 세 번째 필드에서 시작하는 정확한 원시 문자열을 원하면 다음을 사용하십시오.

if (3 in col) Tail = substr (tx, col[3]);

답변3

$1항상 키로 사용하고 행의 나머지 부분(from $2)을 값으로 사용하려면 index다음을 사용하여 해당 위치를 찾을 수 있습니다 $0.

  1. index의 위치를 ​​가져오는 데 사용됩니다 .$1$2
  2. 그런 다음 길이를 사용하여 can이 있는 첫 번째 위치에서 $1부분 문자열을 가져옵니다(can에 복사본이 포함된 경우).$0$2$1$2
  3. 그런 다음 index다시 사용하여 위치를 가져와 $2처음부터 부분 문자열을 추출할 수 있습니다 $2.

예:

# foo.awk
function mysplit(array) {
    pos1 = index($0, $1)
    sub1 = substr($0, pos1 + length($1))
    pos2 = index(sub1, $2)
    sub2 = substr(sub1, pos2)
    array[$1] = sub2
}

{mysplit(arr)}

END {
    for (i in arr) {
        printf "[%s]:   |%s|\n", i, arr[i]
    }
}

귀하의 예를 사용하여 :

% echo -n -e " \tFirst \t\t  Second \t Third  \t" | awk -f foo.awk -F " " | sed 's/\t/\\t/g'
[First]:   |Second \t Third  \t|
% echo -n -e " \tFirst \t\t  Second \t Third  \t" | awk -f foo.awk -F "\t" | sed 's/\t/\\t/g'
[ ]:   |First \t\t  Second \t Third  \t|
% echo -n -e " \tFirst \t\t  Second \t Third  \t" | awk -f foo.awk -F "[ \t]+" | sed 's/\t/\\t/g'
[]:   |First \t\t  Second \t Third  \t|

관련 정보