我有一个文件有一些缺失数据点的值,缺失值显示为****
。我需要选择具有连续 7 列且值小于 10 的行。当我运行我的脚本它还给出了****
连续列中的那些行。
****
我可以通过将所有内容替换为更高的值来轻松解决它。但是,我不想更改我的输入文件。我想做一些事情,以便我的脚本将其视为****
数字(大于10 i.e. str=****=100
)。我怎样才能做到这一点?
示例输入consecutive7pointDown10.input
-
2 3 4 5 6 7 8 0 12 14 23
2 3 4 12 6 7 8 0 1 2 23
**** **** **** **** **** **** **** 8 **** **** 12
我的脚本的结果consecutive7pointDown10.output
-
2 3 4 5 6 7 8 0 12 14 23
**** **** **** **** **** **** **** 8 **** **** 12
但是,预期输出
2 3 4 5 6 7 8 0 12 14 23
我的脚本consecutive7pointDown10
如下-
#!/bin/bash
########################################################################################################################
# This script results rows having at most 10°C in consecutive at most 7 points.
# input = scriptname.input
# output = scriptname.output
########################################################################################################################
input=`basename "$0"`.input
output=`basename "$0"`.output
awk '{
for(i=4;i<=34-6;i++)
{
if($i<=10 && $(i+1)<=10 && $(i+2)<=10 && $(i+3)<=10 && $(i+4)<=10 && $(i+5)<=10 && $(i+6)<=10)
{
print
next
}
}
}' $input > $output
答案1
awk '/(\<[0-9]\s+){7}/{print}' input.txt
或者
sed -rn '/(\b[0-9]\s{1,}){7}/p' input.txt
会做这项工作。
awk 的解释(sed 的逻辑相同):
/(\<[0-9]\s+){7}/{print}
- 打印包含图案的行。\<
- 匹配单词边界;也就是说,如果右侧的字符是“单词”字符,左侧的字符是“非单词”字符,则它匹配。[0-9]\s+
0
-到中的一位数字9
,然后是一个或多个空格。(\<[0-9]\s+){7}
- 如果\<[0-9]\s+
模式重复七次,则匹配。
输入
2 3 4 5 6 7 8 0 12 14 23
2 3 4 12 6 7 8 0 1 2 23
**** **** **** **** **** **** **** 8 **** **** 12
输出
2 3 4 5 6 7 8 0 12 14 23
编辑:
对于一位精度的浮点数(9.2、8.1、7.5 等)。
awk '/(\<[0-9]\.[0-9](\s+|$)){7}/{print}' input.txt
答案2
您可以使用awk
如下方法来避免重复检查 7 个连续列,方法是使用标志在所有满足条件时递增,或者在相反情况下重置它。
awk '{c=0; split($0,arr,/ +/);
for(x in arr) if(arr[x]<10 && arr[x]>=0) {
if(++c==7){ print $0; next } }else{c=0} }' infile
这里我们使用了awk 的 split 函数« split(string, array [, fieldsep [, seps ] ])
» 将行($0
代表 中的整行)拆分为由一个或多个空格分隔的awk
命名数组。arr
接下来循环遍历数组元素并检查其值是否在 10 和 0 之间,然后增加一个名为调用的标志c
,如果达到 7 则打印该行(意味着 7 个连续元素(列)满足条件);否则将标志置为 0。
或者以同样的方式进行操作,而不将行拆分为数组。
awk '{c=0; for(i=1;i<=NF;i++) if($i<10 && $i>=0) {
if(++c==7){ print $0; next } }else{c=0} }' infile
在您的情况下,当您要过滤从第 4 列开始到末尾时,那么您将需要类似的内容。表示NF
每行中的字段/列数,以 开头awk
。
$ time awk '{c=0; for(i=4;i<=NF;i++) if($i<10 && $i>=0) {
if(++c==7) {print $0; next} }else{c=0} }' infile
real 0m0.317s
user 0m0.156s
sys 0m0.172s
或者在正则表达式模式下,再次应用于您的原始文件如果它只包含浮点数,您可以使用下面的命令,该命令比(与标志一起使用的情况下)grep
更高效且快约 6 倍awk
-P
Grep -E、Sed -E - 使用“[x]{1,9999}”时性能较低,但为什么呢?),但考虑到awk
解决方案的灵活性,因为您可以更改范围+如果整数/浮点/两个数字的混合则有效。
$ time grep -P '([^\d]\d\.\d[^\d]){7}' infile
real 0m0.060s
user 0m0.016s
sys 0m0.031s
或者以另一种方式:
$ time grep -P '(\s+\d\.\d\s+){7}' infile
real 0m0.057s
user 0m0.000s
sys 0m0.031s
grep
或,sed
或中的兼容性awk
:
$ time grep -E '([^0-9][0-9]\.[0-9][^0-9]){7}' infile
real 0m0.419s
user 0m0.375s
sys 0m0.063s
$ time sed -En '/([^0-9][0-9]\.[0-9][^0-9]){7}/p' infile
real 0m0.367s
user 0m0.172s
sys 0m0.203s
$ time awk '/([^0-9][0-9]\.[0-9][^0-9]){7}/' infile
real 0m0.361s
user 0m0.219s
sys 0m0.172s