运行 awk 时如何将文件的字符串视为值?

运行 awk 时如何将文件的字符串视为值?

我有一个文件有一些缺失数据点的值,缺失值显示为****。我需要选择具有连续 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-PGrep -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

相关内容