列で「uniq」する方法はありますか?


195

次のような.csvファイルがあります。

stack2@example.com,2009-11-27 01:05:47.893000000,example.net,127.0.0.1
overflow@example.com,2009-11-27 00:58:29.793000000,example.net,255.255.255.0
overflow@example.com,2009-11-27 00:58:29.646465785,example.net,256.255.255.0
...

ファイルから重複した電子メール(行全体)を削除する必要があります(つまりoverflow@example.com、上記の例に含まれる行の1つ)。uniq(カンマで区切られた)フィールド1のみでどのように使用しますか?によるとman、にuniqは列のオプションがありません。

私は何かを試しましたsort | uniqが、うまくいきません。

回答:


325
sort -u -t, -k1,1 file
  • -u ユニークな
  • -t, カンマが区切り文字です
  • -k1,1 キーフィールド1

テスト結果:

overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 

3
列にコンマ自体(引用符付き)が含まれている場合、これは機能しません
user775187

13
なぜ-k1,1に、1が必要なのですか?なぜ-k1だけではないのですか?
hello_there_andy 2014年

18
@hello_there_andy:これはマニュアル(man sort)で説明されています。開始位置と停止位置を表します。
セラーノ2015年

3
@CarlSmotricz:私はそれをテストし、それが何を確認したsortmanページは言うさん: 『 との厳密な順序付けのチェック、;せずに、出力等しい実行の唯一の最初の。』したがって、それは実際に「ソート前の最初の重複の発生」です。-u--unique-c-c
Geremia

2
これは行の順序も変更しますね?
rkachach

102
awk -F"," '!_[$1]++' file
  • -F フィールドセパレータを設定します。
  • $1 最初のフィールドです。
  • _[val]valハッシュ_(通常の変数)を検索します。
  • ++ インクリメントし、古い値を返します。
  • ! 論理否定を返します。
  • 最後に暗黙の印刷があります。

4
このアプローチはソートより2倍高速です
bitek

9
これには、行を元の順序に保つという追加の利点もあります。
AffluentOwl 2015年

8
あなたが必要な場合は、最後の代わりに、uniqのを最初に、このawkスクリプトが役立ちます:awk -F',' '{ x[$1]=$0 } END { for (i in x) print x[i] }' file
スキマスイッチ

3
@eshwarは、辞書インデックスにフィールドを追加するだけです!たとえば!_[$1][$2]++、最初の2つのフィールドでソートするために使用できます。私のawk-fuは、さまざまなフィールドで一意にできるほど強力ではありません。:(
Soham Chowdhury 2017年

1
鮮やかさ!このオプションは行の順序を維持するため、回答よりも優れています
rkachach '20

16

複数の列を検討する。

列1と列3に基づいて一意のリストをソートして指定します。

sort -u -t : -k 1,1 -k 3,3 test.txt
  • -t : コロンはセパレータです
  • -k 1,1 -k 3,3 列1と列3に基づく

8

またはあなたがuniqを使用したい場合:

<mycvs.cvs tr -s ',' ' ' | awk '{print $3" "$2" "$1}' | uniq -c -f2

与える:

1 01:05:47.893000000 2009-11-27 tack2@domain.com
2 00:58:29.793000000 2009-11-27 overflow@domain2.com
1

5
可能な単純化を指摘したいのですが、ダンプできますcat!trにパイプするのではなく、単にtrにを使用してファイルを読み取らせます<。パイプスルーcatは、初心者が使用する一般的な不要な合併症です。大量のデータの場合、パフォーマンスに影響があります。
Carl Smotricz、2009

4
知ってよかった。どうも!(もちろん、これは「猫」と「怠惰」を考えると理にかなっています;))
カーステンC.

フィールドの反転は、を使用して簡略化できますrev
Hielke Walinga

5

あなたが使用することができる重複の最後のものを保持したい場合

 tac a.csv | sort -u -t, -r -k1,1 |tac

それが私の要件でした

ここに

tac ファイルを1行ずつ逆にします


1

これは非常に気の利いた方法です。

最初に、一意性を比較する列が固定幅になるようにコンテンツをフォーマットします。これを行う1つの方法は、フィールド/列幅指定子( "%15s")でawk printfを使用することです。

uniqの-fおよび-wオプションを使用して、前のフィールド/列をスキップし、比較幅(列の幅)を指定できるようになりました。

以下に3つの例を示します。

最初の例では...

1)対象の列を一時的にフィールドの最大幅以上の固定幅にします。

2)-f uniqオプションを使用して前の列をスキップし、-w uniqオプションを使用して幅をtmp_fixed_widthに制限します。

3)列から後続スペースを削除して、その幅を「復元」します(事前に後続スペースがなかったと想定)。

printf "%s" "$str" \
| awk '{ tmp_fixed_width=15; uniq_col=8; w=tmp_fixed_width-length($uniq_col); for (i=0;i<w;i++) { $uniq_col=$uniq_col" "}; printf "%s\n", $0 }' \
| uniq -f 7 -w 15 \
| awk '{ uniq_col=8; gsub(/ */, "", $uniq_col); printf "%s\n", $0 }'

2番目の例では...

新しいuniq列を作成します。1。次に、uniqフィルターが適用された後に削除します。

printf "%s" "$str" \
| awk '{ uniq_col_1=4; printf "%15s %s\n", uniq_col_1, $0 }' \
| uniq -f 0 -w 15 \
| awk '{ $1=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'

3番目の例は2番目の例と同じですが、複数の列が対象です。

printf "%s" "$str" \
| awk '{ uniq_col_1=4; uniq_col_2=8; printf "%5s %15s %s\n", uniq_col_1, uniq_col_2, $0 }' \
| uniq -f 0 -w 5 \
| uniq -f 1 -w 15 \
| awk '{ $1=$2=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'

-3

まあ、awkで列を分離するよりも簡単です。特定のファイルの特定の値を持つすべてのものを削除する必要がある場合は、grep -vを実行しないでください。

たとえば、2行目の値が「col2」であるものをすべて削除するには、col1、col2、col3、col4

grep -v ',col2,' file > file_minus_offending_lines

これが十分でない場合は、一致する値が別の列に表示される可能性があるために一部の行が不適切に削除される可能性があるため、次のようなことができます。

問題の列を分離するawk:例

awk -F, '{print $2 "|" $line}'

-Fはフィールドを「、」で区切って設定します。$ 2は列2を意味し、その後にカスタム区切り文字が続き、その後に行全体が続きます。次に、問題のある値で始まる行を削除してフィルタリングできます。

 awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE

そして、区切り文字の前のものを取り除きます:

awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE | sed 's/.*|//g'

(注-sedコマンドはエスケープ値を含まないため、ずさんです。また、sedパターンは実際には「[^ |] +」のようなもの(つまり、区切り文字以外のもの)である必要があります。


3
彼は行をパージするのではなく、特定の文字列を含む行の単一のコピーを保持したいと考えています。Uniqが適切なユースケースです。
ingyhere 2015年

-3

sort最初にファイルを並べ替えると、を適用できますuniq

それはファイルをうまくソートするようです:

$ cat test.csv
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv | uniq
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

AWKの魔法を使うこともできます。

$ awk -F, '{ lines[$1] = $0 } END { for (l in lines) print lines[l] }' test.csv
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 

これは、質問で尋ねられた列ごとに一意ではありません。これはライン全体でユニークです。また、uniqを実行するために並べ替えを行う必要はありません。2つは相互に排他的です。
Javid Jamae 2014

1
はい、そうです。最後の例は、受け入れられた回答の方がはるかにクリーンであるにもかかわらず、質問が要求したことを実行します。sortその後uniqsort実行する前に行う必要があるuniq、それ以外の場合は動作しません(ただし、2番目のコマンドだけ使用することをスキップすることができますsort -u)。From uniq(1): "INPUT(または標準入力)から隣接する一致する行をフィルターにかけ、OUTPUT(または標準出力)に書き込みます。"
ミカエルS

ああ、あなたはuniqの前にソートすることについて正しいです。uniqが隣接する行でのみ機能することを知りませんでした。私はいつもsort -uだけを使っていると思います。
Javid Jamae
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.