CSVなどのデータリストがありますが、一部の行に値がありません。 Linuxシェルスクリプトを使用して、行の前後に基づいて行方不明の値を生成したいと思います。
この表を例に挙げてみましょう。
ワイヤー | 人々 | 年齢 |
---|---|---|
1 | アダム | 45 |
2 | 単発 | 50 |
サム | シンディ | 47 |
4 | * | # |
5 | エド | 49 |
私がしたいのは、4行48(47と49はC列(各方向から最も近い有効なデータポイント)を意味する)の「*」を埋めることです。
出力:
ワイヤー | 人々 | 年齢 |
---|---|---|
1 | アダム | 45 |
2 | 単発 | 50 |
サム | シンディ | 47 |
4 | シンセ:エド | 48 |
5 | エド | 49 |
私のデータは、任意の行数を含むスペースで区切られたテキストファイル形式です。すべての行は3つの列です。
Forループやgrepなどを理解していますが、通常のLinuxシェルスクリプトでこれを処理する方法がわかりません。
私の考えでは、アスタリスクとハッシュのある行を見つけるために初期パスを実行することです。次に、2 番目の手順を実行し、前と次の行のアスタリスクを (awk '{print $2}'):(awk '{print $2}') に置き換えます。
欠落しているデータが最初または最後の行にある場合は、そのままにしてください。連続行でデータが欠落している場合は、欠落しているすべての行を同じ「Cindy:Ed」と同じ平均に設定できます。 "Cindy:Ed:1" や Cindy:Ed:2" などを設定できればもっと素敵です。
最悪の生の入力の正確な例:(失われた遅延時間を示すために「#」が追加されたトレースパス。)
1 192.168.200.2 1
2 192.168.200.1 1
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 * #
7 11.22.44.66 9
8 * #
9 * #
10 8.8.8.0 25
11 * #
12 * #
13 * #
私が欲しいもの:
1 192.168.200.2 1
2 192.168.200.1 1
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 11.22.33.55:11.22.44.66 7
7 11.22.44.66 9
8 11.22.44.66:8.8.8.0 17
9 11.22.44.66:8.8.8.0 17
10 8.8.8.0 25
11 * #
12 * #
13 * #
答え1
そしてawk
:
#if a previous line with proper IP has been read
oldip != "" {
#i is counter for consecutive invalid lines
i=0
#if IP is set, just print and go to next record
if ($2!="*") {
print ; oldip=$2 ; oldlat=$3 ; next
}
#otherwise get following line and increase counter
else {
#getline exit status => fails for the last line
while (getline > 0) {i++
#check if new line has IP, if so
#set IPold:IPnew and average latency value
if ($2!="*") {
ipfill=oldip":"$2 ; latfill=(oldlat+$3)/2
#print filler lines for all consecutive records without value
for (j=1 ; j<=i ; j++) {
print NR-i+j-1,ipfill,latfill
#alternative printing with oldIP:newIP:counter
# print NR-i+j-1,ipfill":"j,latfill
}
#save current IP+lat and print "good" line
oldp=$2; oldlat=$3
print ; next
}
}
}
#in case getline failed => all previous lines had no value
#just fill them with N/A data as in input
for (j=0 ; j<=i ; j++) {
print NR-i+j,"*","#"
}
}
#If leading lines have no IP value, print them until IP is found
oldip == "" { if ($2=="*") {print ; next} ; oldip=$2 ; oldlat=$3 ; print }
入力する:
1 * #
2 * #
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 * #
7 11.22.44.66 10
8 * #
9 * #
10 8.8.8.0 25
11 * #
12 * #
13 * #
出力:
1 * #
2 * #
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 11.22.33.55:11.22.44.66 7.5
7 11.22.44.66 10
8 11.22.33.55:8.8.8.0 17.5
9 11.22.33.55:8.8.8.0 17.5
10 8.8.8.0 25
11 * #
12 * #
13 * #
計算された行カウンタを使用した代替出力:
1 * #
2 * #
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 11.22.33.55:11.22.44.66:1 7.5
7 11.22.44.66 10
8 11.22.33.55:8.8.8.0:1 17.5
9 11.22.33.55:8.8.8.0:2 17.5
10 8.8.8.0 25
11 * #
12 * #
13 * #
答え2
$ cat tst.awk
$2 == "*" {
buf[++bufSz] = $0
next
}
bufSz > 0 {
split(prev,p)
rng = p[2] ":" $2
val = ($3 + p[3]) / 2
for (i=1; i<=bufSz; i++) {
split(buf[i],flds)
print (prev == "" ? buf[i] : flds[1] OFS rng OFS val)
}
bufSz = 0
}
{
print
prev = $0
}
END {
for (i=1; i<=bufSz; i++) {
print buf[i]
}
}
$ awk -f tst.awk file
1 192.168.200.2 1
2 192.168.200.1 1
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 11.22.33.55:11.22.44.66 7
7 11.22.44.66 9
8 11.22.44.66:8.8.8.0 17
9 11.22.44.66:8.8.8.0 17
10 8.8.8.0 25
11 * #
12 * #
13 * #
答え3
GNU sedは拡張正規表現モード(-E)を使用します。
S='(\S+)'; _re="$S $S"
re="^$_re\\n$_re\$"
_avg='1k\4 \2+2/f'
avg='"$(echo '"'$_avg'"'|dc)"'
sed -E '
s/^(\S+ )[*] #(\n.*\n(.*))/\1\3\2/
ta
s/\n.*//
/[*] #$/b
$!N;//!ba
:loop
${//q;bb}
N;//bloop
:b;h
s/\n.*\n/\n/
s/^\S+ //Mg'"
s#$re#echo '\1:\3' $avg#e
x;G
:a
P;D
" file
Perlで範囲演算子を使用する
perl -lane 'print,next
unless my $e = /\d$/ ... /\d$/;
push @A,[@F]; next
unless $e =~ /E0/ || eof;
if (@A>2&&$A[-1][-1] =~ /\d/) {
my($str,$avg);
for (0,-1) {
$avg += $A[$_][2] / 2.0;
$str .= $A[$_][1] . ":";
}
$str =~ s/.$//;
@{$A[$_]}[1,2] = ($str,$avg)
for 1..$#A-1;
}
print "@$_" for splice @A,0,@A-(eof?0:1);
@A=(); redo if ! eof;
' file
python3 -c 'import sys, itertools as it
prev = ""
p = lambda x: print(*x,sep="",end="")
q = lambda x: x.split()
g = lambda x: x.endswith("* #\n")
with open(sys.argv[1]) as f:
for t in it.groupby(f,g):
G = list(t[1])
if prev == "":
p(G)
if not t[0]: prev = G[-1]
else:
if t[0]: M = G
else:
a,b = map(q,[prev,G[0]])
x = f"{a[1]}:{b[1]}"
y = sum(map(int,[a[2],b[2]]))/2.0
for l in M:
for e in q(l)[0]:
print(e,x,y)
p(G); prev = G[-1]
p(M)
' file
$ cat file
1 * #
2 * #
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 * #
7 11.22.44.66 10
8 * #
9 * #
10 8.8.8.0 25
11 * #
12 * #
13 * #
出力;
1 * #
2 * #
3 10.10.10.1 1
4 11.22.33.44 2
5 11.22.33.55 5
6 11.22.33.55:11.22.44.66 7.5
7 11.22.44.66 10
8 11.22.44.66:8.8.8.0 17.5
9 11.22.44.66:8.8.8.0 17.5
10 8.8.8.0 25
11 * #
12 * #
13 * #
答え4
var1=$(awk '{a[++i]=$0}/#/{for(x=NR-1;x<NR;x++)print a[x]}' file.txt | awk '{print $NF}')
var2=$(awk '/#/{x=NR+1}(NR==x){print $NF}' file.txt)
sed -i "s/#/$var3/g" file.txt
sed -i "s/\*/Cindy:Ed/g" file.txt
output
cat file.txt
line person age
1 Adam 45
2 Bob 50
3 Cindy 47
4 Cindy:Ed 48
5 Ed 49