我是bash和linux编程的新手。我有一个小问题。
对于特定的截止点(c),我想转储一个文件,如果两个连续的值都在c之上,则该文件将打印出c之上的值。例如
x y
1 0.34
2 0.3432
3 0.32
4 0.35
5 0.323
6 0.3623
7 0.345
如果c = 0.33,它将打印出第2列
0.34
0.3432
0.3623
0.345
尽管它超出了截止值0.33,但它不会打印出0.35,因为0.35之后的下一个值为0.323,这使参数“两个连续的值都高于c”失败。
答案 0 :(得分:1)
原始问题: 打印2个或多个连续值满足给定条件的所有序列
以下应该可以工作:
awk 'p || (prev>c && $2>c && NR>2){print prev}
{ p = (prev>c && $2>c); prev=$2 }
END{if(p) print $2 }' c=0.33 <file>
它具有以下逻辑:
p
会跟踪是否已打印上一行。如果已打印,则也应打印当前行。p==0
),则应检查是否应在(prev>c && $2>c)
上打印前一行p
,并将prev
设置为当前值p==1
打印最后一个值。您基本上总是落后一线。
解决此问题的另一种方法是检查该值是否满足条件并将其存储在数组中。如果遇到不满足条件的值,请处理该数组。这会占用更多的内存:
awk '(NR==1){next}
($2>c) { a[NR]=$2; next }
(length(a) == 1) { delete a[NR-1]; next }
{ for(i=NR-length(a);i<NR;++i) {print a[i]; delete a[i]} }
END { if (length(a)>1) for(i=NR+1-length(a);i<=NR;++i) {print a[i]} }
' c=0.33 <file>
第二个问题: 打印$ 2的连续值的子集,其中m
或更多的值满足条件cond
,并且最多n
个连续值值不满足cond
。序列的开始和结束的值满足cond
以下awk
脚本将执行此操作。不要忘记根据自己的意愿调整值m
,n
和c
并更新条件函数。
function cond(val) { return val > c }
BEGIN{c=0.33; m=2; n=1}
# skip the header
(NR==1){next}
# if no values satisfy cond ...
(M==0 && !cond($2)) { next }
# ... otherwise continue from here
{ a[NR]=$2 }
# set counters M and N (M satisfy cond, N not )
cond($2) { M++; N=0 }
!cond($2) { N++ }
# This sequence failed, delete it
(N>n && M<m) { for(i in a) delete a[i]; M=0; N=0 }
# This sequence is OK, strip it and print it
(N>n) { j=NR; while (!cond(a[j])) delete a[j--]
for (i=j+1-length(a);i<=j;++i) { print a[i]; delete a[i] }
M=0; N=0 }
# Check if the final stored sequence is successful
END { if (M>=m) {
j=NR; while (!cond(a[j])) delete a[j--]
for (i=j+1-length(a);i<=j;++i) print a[i]
}
}
答案 1 :(得分:0)
Perl解决方案:
c=.33 m=2 perl -lane '
if ($F[1] > $ENV{c}) { push @r, $F[1] }
else {
if (@r >= $ENV{m}) { print for @r }
@r = ();
}
END { if (@r >= $ENV{m}) { print for @r } }' -- file
它将连续值存储到数组@r中,如果当前值小于阈值,则在足够长的情况下打印该数组。
-l
从输入中删除换行符并将其添加到输出中-n
逐行读取输入-a
将每一行自动拆分为@F数组如果序列往往很长,则只能将前m
个元素存储在数组中以节省一些内存。
if ($F[1] > $ENV{c}) {
push @r, $F[1];
print shift @r if @r > $ENV{m};
} else {
if (@r >= $ENV{m}) { print for @r }
@r = ();
}
END { if (@r >= $ENV{m}) { print for @r } }'
答案 2 :(得分:0)
您可以使用以下awk脚本:
awk -v cutoff="0.33" '
$2>cutoff{
if(prev)
{print prev ORS $2 }
else
{prev=$2;next}
}
{prev=""}' file
如果超出临界值,它将在prev
变量中存储该值,并将其重置为下一个数字。
答案 3 :(得分:0)
在awk中使用Bash参数的方式如下:
$ c=2.3
$ awk -v c="$c" 'BEGIN{print c}'
2.3
然后您可以使用它来编写脚本,如下所示:
c=0.33
m=2
awk -v c="$c" -v m="$m" '($2+0!=$2) {next}
$2+0<c {cnt=0; split("",lst); next}
$2+0>=c && cnt<m {lst[++cnt]=$2}
$2+0>=c && cnt==m {for (i=1; i<=m; i++) print lst[i]
cnt=0; split("",lst)}' file
这不会显示重叠范围,例如:
1 0.34
2 0.3432 # prints 0.34\n0.3432\n here
3 0.35 # unclear if it should print 0.3432\n0.34\n here....
给出更新,将打印连续的行。
给出:
$ cat file
x y
1 0.34
2 0.3432
2a 0.35
3 0.32
4 0.35
5 0.323
6 0.3623
7 0.345
您可以这样做:
c=0.33
m=2
awk -v c="$c" -v m="$m" '($2+0!=$2) {next}
$2+0>=c {lst[++cnt]=$2; next}
$2+0<c { if (cnt>=m) for (i=1; i<=cnt; i++) print lst[i]
cnt=0; split("",lst); next}
END{if (cnt>=m) for (i=1; i<=cnt; i++) print lst[i]}' file
打印:
0.34
0.3432
0.35
0.3623
0.345