列ごとに「uniq」する方法はありますか?
1. 概要
uniq コマンドは、重複した隣接する行を入力から削除するための便利なユーティリティであることがわかっています。
ただし、CSVファイルなどの列ベースの入力ファイルを処理する場合は、列が重複している行を削除したい場合があります。
uniq コマンドは、行全体が同じである場合にのみ行を削除するため、この場合は役に立ちません。
このチュートリアルでは、行全体ではなく列に対して「uniq」操作を実行する方法を学習します。
2. 問題の紹介
まず、例としてCSVファイルprice.csvを作成しましょう。
Product,Price,Update
Monitor,218,2019-01-01
Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02
Keybord,22,2019-03-02
Wireless Mouse,30,2019-03-02
Keybord,18,2019-04-02
Monitor,208,2019-02-01
Keybord,25,2019-05-02
Monitor,230,2019-03-01
price.csv ファイルには、3つの列があり、各行には、各製品の価格が更新されたときが記録されます。
製品が重複している行を削除したいと思います。
したがって、product列で「uniq」操作を行います。
問題を解決する2つの方法を見て、2つのアプローチを比較してみましょう。sortとawk。
3. sortを使用する
sort コマンドは、特定のフィールドで行をソートし、ソート結果から重複を削除できます。 重複の場合、最初のインスタンスのみが保持されます。
このセクションでは、sortコマンドを使用して問題を解決します。
price.csv ファイルでは、最初の行がヘッダー行であり、データを並べ替えるときにヘッダーを並べ替えに参加させたくないことがわかります。
これを行うには、 tail コマンドを使用して最初の行を切り取り、残りをsortコマンドにパイプします。
$ tail -n+2 price.csv | sort -u -t, -k1,1
Keybord,20,2019-02-02
Monitor,218,2019-01-01
Wireless Mouse,25,2019-02-02
上記のコマンドを詳しく見て、その各部分を理解しましょう。
- tail -n + 2 :2行目からファイルの終わりまで取得します
- sort -u :重複を並べ替えて削除します
- -t、:フィールド区切り文字としてコンマを定義します
- -k1,1 :最初の列で並べ替え
おそらく、出力にヘッダー行を残したいのです。
これを実現するには、 headコマンドで最初の行を最初に出力してから、上記のコマンドを実行します。
$ head -n1 price.csv && tail -n+2 price.csv | sort -u -t, -k1,1
Product,Price,Update
Keybord,20,2019-02-02
Monitor,218,2019-01-01
Wireless Mouse,25,2019-02-02
4. awkを使用する
awk は、*nixシステムの非常に強力なコマンドラインテキスト処理ツールです。
この問題に簡潔に対処できます。
$ awk -F, 'NR==1 || !a[$1]++' price.csv
Product,Price,Update
Monitor,218,2019-01-01
Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02
sort コマンドを使用したソリューションと比較すると、awkコマンドははるかにコンパクトです。 よく見てみましょう。
-F、は、フィールド区切り文字としてコンマを設定します。
NR == 1 式はヘッダー行を処理します:awkは出力の最初の行を出力します。
ここで注意が必要な部分があります。!a [$ 1] ++、それがどのように機能するかを理解しましょう。
まず、awkでは、ゼロ以外の数値 patternはtrue、と評価され、trueパターンは
例に戻ります。
- 「Keyboard」の行が初めてawkに来ると、 awkは連想配列要素を作成します: a [ 「キーボード」]デフォルト値: 0
- a [“ Keyboard”] ++ は元の値を返し、その値を1ずつインクリメントするため、式は 0 を返し、 a [“ Keyboard”]が得られます。 = 1
- !a [“ Keyboard”] ++ は!0 になります。これまでに学んだように、 trueと評価され、がデフォルトのアクションをトリガーします。 :現在の行を印刷します
- 「Keyboard」の行が再び来ると、配列要素はすでに存在し、 a [“Keyboard”]++は1を返し、2を保持します
- !a [“ Keyboard”] ++ 今回は!1 になるため、 false :何もしません。
このように、最初の「 Keyboard 」が印刷された後、配列要素 a [“ Keyboard”]は正の数を保持します。 次のすべての行に「 キーボード ” 作る予定です !a[「キーボード」]++ 次のように評価します
5. awk対。 ソート
これまで、この問題を解決するための2つの異なるアプローチを見てきました。 それらの出力を比較すると、それらが異なることがわかります:
$ diff -y <(head -n1 price.csv && tail -n+2 price.csv | sort -u -t, -k1,1) <(awk -F, 'NR==1 || !a[$1]++' price.csv)
Product,Price,Update Product,Price,Update
Keybord,20,2019-02-02 <
Monitor,218,2019-01-01 Monitor,218,2019-01-01
> Keybord,20,2019-02-02
Wireless Mouse,25,2019-02-02 Wireless Mouse,25,2019-02-02
これは、 sortコマンドが最初にキー列で行をソートするためです。これにより、行の元の順序が変更される可能性がありますが、awkは変更されません。
2つのソリューションを確認して比較してみましょう。
- 入力にヘッダー行がある場合、awkはsortアプローチよりもはるかに簡単にヘッダー行を処理できます。
- awk は単一のプロセスで問題を解決しますが、 sort アプローチには、 head 、 tail、、
sort[の3つのプロセスが必要です。 X152X] - awk は、 awk ソリューションがファイルをソートしないため、sortアプローチよりも高速です。
- ソートされた結果が必要な場合は、sortアプローチが正しい選択です。
6. 結論
この記事では、最初に列問題による「uniq」を紹介しました。 次に、sortユーティリティとawkコマンドを使用してそれを解決する方法を学びました。
最後に、2つのアプローチを比較して、さまざまな要件に応じて適切なアプローチを選択する方法を理解しました。