特定の列(例では$ 2)から重複フィールド(カンマ区切り)を削除したいのですが。
入力ファイル:
A 1,2,3,4
B 4,5,6,3
C 2,15
期待される出力:
A 1,2,3,4
B 5,6
C 15
承認された回答:
perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e'
上記のように実行できます:
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
仕組み
最初にperl
を呼び出す -lpe
を使用 次の3つのことを行います。
-
-l[octal]
行終了処理を有効にし、行ターミネータを指定します -
-p
-nのようなループを想定しますが、sedのように行も出力します -e program
1行のプログラム(複数の-eを許可、programfileを省略)
これは基本的にファイルを取り込み、改行を取り除き、行を操作し、完了したら改行文字をファイルに戻します。つまり、ファイルをループして、それぞれに対してPerlコードを順番に実行するだけです。
実際のPerlコードについて:
-
s
間隔文字を意味します(5文字の[ fnrt]
およびv
perl
の新しいバージョン 、[[:space:]]
のように 。 -
K
Kの左側にあるものを保持し、$&に含めないでください -
S+
セットに含まれていない1つ以上の文字[fnrtv]
join ",",
結果を取得し、各フィールドをカンマで区切るように再結合します。
split ",", $&
S+
によって検出された一致を取得します カンマなしでフィールドだけに分割します。
grep {!$seen{$_}++}
各フィールドの番号を取得し、ハッシュに追加します。$seen{}
ここで、各フィールドの番号は$_
それぞれを見ていきます。フィールド番号が「表示」されるたびに、++
を介してカウントされます 演算子、$seen{$_}++
。
grep {!$seen{$_}++}
一度しか表示されなかった場合、フィールド値を返します。
何が起こっているかを確認するために変更
この変更された忌まわしきものを使用すると、このPerlワンライナーがファイルの行を横切って移動するときに何が起こっているかを確認できます。
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile
keys: 4 1 3 2 | vals: 1 1 1 1
A 1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B 5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C 15
これは、$seen{}
の内容を示しています。 ファイルからの行の処理の最後に。ファイルの2行目を見てみましょう。
B 4,5,6,3
そして、これが私の修正バージョンがその行を次のように示しているものです:
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
つまり、フィールド#6(1回)、フィールド#4(2回)など、およびフィールド#5(1回)を見たことがあるということです。したがって、grep{...}
結果を返します。この配列がこの行に存在する場合(4,5,6,3)、および1回だけ表示された場合(6,1,15,5)にのみ、この配列からの結果が返されます。これら2つのリストの共通部分は(5,6)であるため、grep
によって返されるのはそれです。 。
参考資料
- perlre – perldoc.perl.org