입력 행을 구문 분석한 후 awk는 원래 행( $0
)과 각 개별 열( $1
, $2
, ...)에 대한 액세스를 제공합니다. 이 프로세스를 수행하는 동안(요청 시 느리게) 두 번째 열의 문자 위치가 시작되는 위치를 정확히 알고 있습니다.
- 이 정보에 대한 액세스를 제공합니까(예: 원래 행 $0에서 두 번째 열이 시작되는 위치)?
- 그렇지 않다면 올바르게 찾을 수 있는 건전하고 우아한 방법이 있습니까? (동적 정규식 사용
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
구축하여 각 필드와 구분 기호의 길이를 누적할 수 있습니다.start
size
이 코드는 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
.
index
의 위치를 가져오는 데 사용됩니다 .$1
$2
- 그런 다음 길이를 사용하여 can이 있는 첫 번째 위치에서
$1
부분 문자열을 가져옵니다(can에 복사본이 포함된 경우).$0
$2
$1
$2
- 그런 다음
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|