112

こことほぼ同じ質問があります。

などを含む配列がありますaa ab aa ac aa ad。この配列からすべての一意の要素を選択したいと思います。他の質問で述べたように、これは単純であると思いsort | uniqましsort -uたが、配列は何も変更されていません...コードは次のとおりです。

echo `echo "${ids[@]}" | sort | uniq`

私は何を間違っていますか?

4

16 に答える 16

159

少しハックですが、これでうまくいくはずです:

echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

並べ替えられた一意の結果を配列に保存するには、配列の割り当てを行います。

sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

シェルがヒアストリング ( should) をサポートしている場合bash次のechoように変更することでプロセスを節約できます。

tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

2021 年 8 月 28 日時点のメモ:

ShellCheck wiki 2207によるとread -a、分割を避けるためにパイプを使用する必要があります。したがって、bash のコマンドは次のようになります。

IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"

また

IFS=" " read -r -a ids <<< "$(tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ')"

入力:

ids=(aa ab aa ac aa ad)

出力:

aa ab ac ad

説明:

  • "${ids[@]}"- シェル配列を操作するための構文 (一部echoまたはヒアストリングとして使用される場合)。@部分は「配列内のすべての要素」を意味します
  • tr ' ' '\n'- すべてのスペースを改行に変換します。配列は、スペースで区切られた単一行の要素としてシェルに表示されるためです。また、ソートは入力が別々の行にあることを期待しているためです。
  • sort -u- 一意の要素のみを並べ替えて保持する
  • tr '\n' ' '- 前に追加した改行をスペースに戻します。
  • $(...)-コマンド置換
  • 余談:tr ' ' '\n' <<< "${ids[@]}"より効率的な方法です:echo "${ids[@]}" | tr ' ' '\n'
于 2012-11-30T15:45:46.560 に答える
37

Bash バージョン 4 以降を実行している場合 (最新バージョンの Linux ではこれに該当するはずです)、元の配列の各値を含む新しい連想配列を作成することにより、bash で一意の配列値を取得できます。このようなもの:

$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad

これが機能するのは、任意の配列 (任意の言語の連想配列または従来の配列) では、各キーが 1 回しか表示されないためです。forループが in の 2 番目の値に到達すると、元々設定されていたaaina[2]を上書きします。b[aa]a[0]

ネイティブの bash で処理を行うと、パイプや や などの外部ツールを使用するよりも高速になるsort可能uniq性がありますが、大規模なデータセットの場合は、awk、python などのより強力な言語を使用すると、パフォーマンスが向上する可能性があります。

自信がある場合は、複数の引数の形式をリサイクルする の機能をfor使用してループを回避できますが、これには が必要なようです。(それでもよろしければ、ここで読むのをやめてください。)printfeval

$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )

このソリューションが必要な理由evalは、単語分割の前に配列値が決定されるためです。つまり、コマンド置換の出力は、一連のキー=値のペアではなく、1 つの単語と見なされます。

これはサブシェルを使用しますが、bash ビルトインのみを使用して配列値を処理します。evalの使用を批判的な目で評価してください。chepner、glenn jackman、greycat があなたのコードに間違いを見つけられないという 100% の確信がない場合は、代わりに for ループを使用してください。

于 2012-11-30T16:40:16.390 に答える
26

これはすでに回答済みですが、検索結果でかなり上位に表示されており、誰かの助けになるかもしれません.

printf "%s\n" "${IDS[@]}" | sort -u

例:

~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo  "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>
于 2013-07-10T05:12:47.360 に答える
12

'sort' を使用して、for ループの出力を並べ替えることができます。

for i in ${ids[@]}; do echo $i; done | sort

「-u」を使用して重複を排除します。

for i in ${ids[@]}; do echo $i; done | sort -u

最後に、一意の要素で配列を上書きすることができます:

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
于 2015-09-14T15:02:48.247 に答える
3

猫番号.txt

1 2 3 4 4 3 2 5 6

行を列に出力:cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'

1
2
3
4
4
3
2
5
6

重複レコードを見つけます。cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'

4
3
2

重複レコードを置換:cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'

1
2
3
4
5
6

Uniq レコードのみを検索します。 cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

1
5
6
于 2016-10-06T12:54:29.437 に答える
3

bash 内部のみを使用するソリューションが必要な場合は、値を連想配列のキーとして設定し、キーを抽出できます。

declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do 
  uniqs["${f}"]=""
done

for thing in "${!uniqs[@]}"; do
  echo "${thing}"
done

これは出力されます

bar
foo
bar none
于 2017-01-11T14:42:09.237 に答える