UNIXシェルスクリプトのリストから一意の値または個別の値を選択します


238

値の長いリストを返し、改行で区切られたkshスクリプトがあり、一意の値または個別の値のみを表示したい。これは可能ですか?

たとえば、私の出力がディレクトリのファイルサフィックスであるとします。

tar
gz
java
gz
java
tar
class
class

次のようなリストを表示したい:

tar
gz
java
class

回答:


431

uniqsortアプリケーションを確認することをお勧めします。

./yourscript.ksh | 並べ替え| uniq

(参考までに、このコマンドラインでは並べ替えが必要です。並べ替えは、uniq直後にある重複する行のみを削除します)

編集:

のコマンドラインオプションに関して、Aaron Digullaが投稿したものとは異なりuniqます。

次の入力があるとします。

クラス
瓶
瓶
瓶
置き場
置き場
java

uniq すべての行を1回だけ出力します。

クラス
瓶
置き場
java

uniq -d 複数回出現するすべての行を出力し、それらを1回出力します。

瓶
置き場

uniq -u 正確に一度だけ現れるすべての行を出力し、それらを一度印刷します:

クラス
java

2
後発者のための単なる参考:@AaronDigullaの回答は修正されました。
mklement0 2014年

2
非常に良い点この `この並べ替えはこのコマンドラインで必要です。uniqは、私が学んだばかりの、互いに直後にある重複した行のみを削除します。
HattrickNZ 2015

4
GNU sort-u、独自の値を与えるためのバージョンも備えています。
Arthur2e5 2015

uniq縫い目が隣接するラインのみを処理するように(少なくともデフォルトでは)、sortフィードする前に入力できることを理解しましたuniq
Stphane 2016

85
./script.sh | sort -u

これは一酸化炭素の 回答と同じですが、もう少し簡潔です。


6
あなたは控えめです:あなたのソリューションはより良いパフォーマンスを発揮します(おそらく大きなデータセットでのみ顕著になります)。
mklement0 2014年

... | sort | uniqワンショットで実行するよりも効率的だと思います
Adrian Antunez

10

並べ替えが望ましくない可能性がある大規模なデータセットの場合は、次のperlスクリプトを使用することもできます。

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

これは基本的にすべての行出力を記憶するだけなので、再度出力されません。

sort | uniq事前に並べ替えが必要ないという点で、「」ソリューションよりも優れています。


2
非常に大きなファイルの並べ替え自体は、sortの問題ではないことに注意してください。使用可能なRAM +スワップよりも大きいファイルをソートできます。Perl、OTOHは、重複がほとんどない場合に失敗します。
アーロンディグラ2009年

1
はい、予想されるデータによってはトレードオフになります。Perlは、多数の重複がある巨大なデータセットに適しています(ディスクベースのストレージは不要)。重複の少ない巨大なデータセットでは、ソート(およびディスクストレージ)を使用する必要があります。小さなデータセットはどちらでも使用できます。個人的には、まずPerlを試して、失敗した場合はソートに切り替えます。
paxdiablo 2009年

並べ替えは、ディスクにスワップする必要がある場合にのみメリットがあります。
paxdiablo 2009年

5
これは、すべての行の最初の出現が必要な場合に最適です。並べ替えはそれを壊すでしょう。
Bluu

10

zshのあなたはこれを行うことができます。

% cat infile 
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class

または、AWKを使用できます。

% awk '!_[$0]++' infile    
tar
more than one word
gz
java
class

2
入力のソートを含まない賢いソリューション。警告:非常に賢いが暗号化されたawkソリューション(説明については、stackoverflow.com / a / 21200722/45375を参照)は、一意の行の数が十分に小さい限り(一意の行がメモリに保持されている限り)、大きなファイルで動作します。 )。zsh解決策は、大きなファイルとオプションではないかもしれない、最初にファイル全体をメモリに読み込みます。また、書かれているように、スペースが埋め込まれていない行のみが正しく処理されます。これを修正するには、IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}代わりに使用してください。
mklement0 2014年

正しい。または:(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Dimitre Radoulov 2014年

1
おかげで、それはより簡単です(サブシェルの外で必要な変数を設定する必要がないと仮定します)。[@]配列のすべての要素を参照するためにサフィックスがいつ必要かについて興味があります-少なくともバージョン5以降では、それがなくても機能します。それともわかりやすくするために追加しただけですか?
mklement0 2014年

1
@ mklement0、あなたは正しいです!記事を書いているときは思いもしませんでした。実は、これは十分なものでなければならない:print -l "${(fu)$(<infile)}"
Dimitre Radoulov

1
素晴らしい、投稿を更新してくれてありがとう-私awkもサンプル出力を修正する自由を取りました。
mklement0 2014年

9

それらをsortとを通してパイプしますuniq。これにより、すべての重複が削除されます。

uniq -d重複のみをuniq -u与え、一意のものだけを与えます(重複のストリップ)。


外観で最初にソートする必要がある
ブラスター

1
そうです。より正確には、重複するすべての行をグループ化する必要があります。ソートによる定義はこれですが;)
Matthew Scharley 2009年

また、これuniq -uはデフォルトの動作ではありません(詳細については私の回答の編集を参照してください)
Matthew Scharley 2009年

7

AWKを使用すると、並べ替えよりも早く見つけることができます

 ./yourscript.ksh | awk '!a[$0]++'

これは間違いなく私のお気に入りの方法です。特に大きなファイルの場合、sort | uniq-solutionsはおそらく望んでいるものではありません。
Schmitzi

1

要求に応じて一意です(ただし、ソートされません)。
(時間をかけてテストされたように)要素が70未満の場合、システムリソースの使用量が少なくなります。
stdinから入力を受け取るように作成
(または変更して別のスクリプトに含める):(
Bash)

bag2set () {
    # Reduce a_bag to a_set.
    local -i i j n=${#a_bag[@]}
    for ((i=0; i < n; i++)); do
        if [[ -n ${a_bag[i]} ]]; then
            a_set[i]=${a_bag[i]}
            a_bag[i]=$'\0'
            for ((j=i+1; j < n; j++)); do
                [[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
            done
        fi
    done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
    a_bag[i]=$e
    i=$i+1
done
bag2set
echo "${a_set[@]}"

0

ファイル内の重複しないエントリを取得するためのより良いヒントが得られます

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.