awk:使用列变量作为模式

时间:2017-02-16 15:41:22

标签: awk

我有一个类似值的表格,不幸的是格式不同(不在我的控制范围内),而且我只想要那些$ 1和$ 2完全不同的行。我关注的两个主要问题是:

1)我没有运气

awk '$1 !~ /$2/' filename

甚至部分完成这项任务;它产生一个空集。我感觉这是我写/ $ 2 /部分的方式,但是找不到不会产生空集或错误的格式。

2)整个电路板的格式差异并不相同。以下是输入的示例:

q12345      12345
Q012345     D66666
q12345      Q12345
Q012345     12345
q12345      23588

我只想返回具有明显不同值的行,如下所示:

Q012345    D666666
q12345     23588

一条希望似乎是,如果字母有时会忽略前面的0,那么每对列都有相同的数字序列。任何帮助,将不胜感激。如果它有帮助,那就是korn shell。

更新:我看到我犯了一个常见的错误,就是假设每个人都知道我在说什么,没有充分的理由。通过“明显不同”,我的意思是前面0的值中的数字是不同的。输入后,我意识到这些字母对于我对此数据执行的特定任务实际上毫无意义。所以q12345和12345对于我的目的是相同的,012345和12345是相同的,但12345和78945不是,也不是12345和12346。

既然我输入了这个,那么只有一种简单的方法可以只返回每列中的数字,这样才能比较数字吗?这样,前面的零将毫无意义(012345 = 12345),我会得到我想要的东西。抱歉有任何困惑。

4 个答案:

答案 0 :(得分:1)

如果模式包含在变量中,请不要使用斜杠 - 使用斜杠来包含静态正则表达式。你想要

awk 'tolower($1) !~ tolower($2)' filename

使用tolower启用不区分大小写的匹配。或者,如果您使用的是GNU awk:

gawk -v IGNORECASE=1 '$1 !~ $2' filename

答案 1 :(得分:0)

嗯,这确实取决于你对'完全不同'的意思。我的意思是,你可以通过以下方式前进和后退子串匹配:

#!/usr/bin/env perl
use strict;
use warnings;

while ( <DATA> ) {
    my ( $first, $second ) = split;
    print unless ($first =~ /$second/i or $second =~ /$first/i);
}

__DATA__
q12345      12345
Q012345     D66666
q12345      Q12345
Q012345     12345
q12345      23588

哪会给你:

Q012345     D66666
q12345      23588

这个单行 - 如果:

perl -lane 'print unless ( $F[0] =~ /$F[1]/ or $F[1] =~ /$F[0]/ )'

或者你可以根据'Levenshtein距离'来做到这一点:

#!/usr/bin/env perl
use strict;
use warnings;

use Text::Levenshtein qw(distance);;

while ( <DATA> ) {
    my ( $first, $second ) = split;
    print unless distance ( $first, $second ) < 3;
}

__DATA__
q12345      12345
Q012345     D66666
q12345      Q12345
Q012345     12345
q12345      23588

注 - Q012345 - &gt; 12345是Levenshtein距离2,因此您可以调整相似度。

注意 - 我知道您已标记awk并询问korn shell。我给了perl,因为当“korn”或“awk”都是时,通常

您可以替换上面的__DATA__,这对于创建自包含示例非常有用:

while ( <> ) { 
    my ( $first, $second ) = split;
    #etc .
}

<>是神奇的文件句柄,就像你期望的那样工作grep,sed或awk - 读取stdin或命令行中指定的文件,这样你就可以:

cat somefile | script.pl

script.pl somefile

在任何一种情况下它都会做正确的事。

答案 2 :(得分:0)

鉴于修改后的描述,这似乎完成了工作(示例数据位于我的机器上名为data的文件中),但我承认可能有更紧凑的方法来实现相同的结果:

$ awk 'substr($1, match($1, /[0-9]+/)) +0 != substr($2, match($2, /[0-9]+/)) + 0 { print }' data
Q012345     D66666
q12345      23588
$

为POSIX awk定义了matchsubstr函数。 match函数返回第一个参数中正则表达式开头的偏移量,因此它返回$1$2中第一个数字的索引。 substr返回从该位置开始的字符串。 + 0确保以数字方式处理值(因此忽略前导零) - 如果没有,则报告Q012345 12345行。

在Mac上测试(macOS Sierra 10.12.13,原生(BSD)awk和GNU awk)。

  

我认为我遇到了类似于下面另一张海报答案的情况,我可能会对Awk有一些奇怪/旧的实现。代码一直返回非法语句错误。此版本的Awk没有match ...

这只适用于sub函数,该函数将正则表达式应用于变量并替换匹配的内容,在本例中为空字符串,从而删除字段开头的非数字(或,如果开头有数字,但后面有非数字,它会删除那些;天堂会帮助你,如果你有一个字段1234-5678-99,因为你最终会将12345678与另一个字段进行比较)。还有gsub,它会反复应用搜索和替换。

$ awk '{ v1 = $1; sub(/^[^0-9]*/, "", v1); v2 = $2; sub(/^[^0-9]*/, "", v2); if (v1 + 0 != v2 + 0) print }'  data
Q012345     D66666
q12345      23588
$

如果您还没有subgsub,那么(a)请确定平台 - o / s和版本 - 以及Awk的版本,以及(b)请获取并安装GNU Awk所以你没有这样的未来。如果这是一个问题,请提供您所拥有的Awk版本的在线文档的链接,并且很可能是另一种解决方案。

如果您使用的是Solaris,请尝试nawk(新的Awk)而不是oawk(旧的Awk) - 其中awk可能是指向oawk或{的链接{1}}。如果这是问题,请重新调整系统以使nawk成为默认值。

答案 3 :(得分:0)

也许我误解了这个问题,但似乎你只需要:

$ awk '{x=$0; gsub(/[^0-9 \t]/,"")} $1!=$2{print x}' file
Q012345     D66666
q12345      23588