入力行を解析した後、awkは元の行($0
)と各個々の列($1
、、、$2
...)へのアクセスを提供します。このプロセスを実行している間(要求が遅くなります)、2番目の列の文字位置が始まる場所を正確に知っています。
- この情報へのアクセスを提供しますか(たとえば、元の行$ 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
ここ - 2番目の列()が文字で始まることに注意してください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
ここ - 2番目の列()が文字で始まることに注意してくださいF
。3番目の場所入力行の文字 - (スペース)をキーとして保存し、
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
ここ - 2番目の列()が文字で始まることに注意してくださいF
。3番目の場所行に文字を入力します。これにより、最初の列が空の文字列であることがわかり、後で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番目の列()が文字で始まることに注意してください2
。7番行に文字を入力してください。これにより、後で使用できるように-11
キーと値で保存できます。22;,;..;33-44...;
デフォルトでは、アイデアはカスタム使用のためにいくつかの(最初の)列を取得し、行の残りの部分(2番目の列から行の終わりまで)をそのまま保持(変数に保存)することです。
答え1
Split() の 4 番目の引数として 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<
$
たとえば、3番目のフィールドで始まる正確な生の文字列が必要な場合は、次のようにします。
if (3 in col) Tail = substr (tx, col[3]);
答え3
$1
常にキーとして使用し、行の残りの部分$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|