解析输入行后,awk 提供对原始行 ( $0
) 以及每个单独列 ( $1
, $2
, ...) 的访问。在执行此过程时(惰性地,按需) - 它确切地知道第二列开始的字符位置。
- 它是否提供对此信息的访问(即,第二列从原始行 $0 中的哪个位置开始)?
- 如果没有 - 有什么理智/优雅的方法可以正确找到它吗? (我即将开始编写一种丑陋且低效的方式来模仿 awk 的内部行为,方法是使用基于 的动态正则表达式
FS
、处理特殊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
使用 GNU awk 作为 split() 的第四个参数:
$ 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
作为键,并将该行的其余部分(来自$2
)用作值,那么您可以使用来index
查找它们所在的位置$0
:
- 用于获取in
index
的位置$1
$2
- 然后使用 的 长度来获取第一个位置 where 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|