簡體   English   中英

awk:使用列變量作為模式

[英]awk: using column variable as a pattern

我有一個類似值的表格,不幸的是格式不同(我的控制之外),我想只想要那些$ 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),我會得到我想要的東西。 對不起任何困惑。

如果模式包含在變量中,請不要使用斜杠 - 使用斜杠來包含靜態正則表達式。 你要

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

使用tolower啟用不區分大小寫的匹配。 或者如果您使用的是GNU awk:

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

嗯,它確實取決於你所說的“完全不同”的含義。 我的意思是,你可以通過以下方式前進和后退子串匹配:

#!/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 - > 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

在任何一種情況下它都會做正確的事情。

鑒於修改后的描述,這似乎完成了工作(示例數據位於我的機器上稱為data的文件中),但我承認可能有更緊湊的方法來實現相同的結果:

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

matchsubstr函數是為POSIX awk定義的。 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可能是oawknawk的鏈接。 如果這是問題,請重新調整系統以使nawk成為默認值。

也許我誤解了這個問題,但似乎你只需要:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM