Bashの文字列/配列から一意の要素配列を作成するには?

Bashの文字列/配列から一意の要素配列を作成するには?

文字列「1 2 3 2 1」または配列[1,2,3,2,1]がある場合は、一意の値をどのように選択しますか?

"1 2 3 2 1" produces "1 2 3" 

または

[1,2,3,2,1] produces [1,2,3]

uniqに似ていますが、uniqはライン内のパターンではなくライン全体に適用されるようです。

答え1

zshを使用する場合:

$ array=(1 2 3 2 1)
$ echo ${(u)array[@]}
1 2 3

または(KSH_ARRAYSオプションが設定されていない場合)

$ echo ${(u)array}
1 2 3

答え2

GNUを使用するawk(元の順序も維持されます)

printf '%s\n' "1 2 3 2 1" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}'
1 2 3 

read配列bashによって

read -ra arr<<<$(printf '%s\n' "1 2 3 2 1" |
 awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
printf "%s\n"  "${arr[@]}"
1
2
3

答え3

bash組み込み演算子がないため、任意の値を持つ配列の場合、これは非常に面倒です。

bashただし、変数にNUL文字を格納することはサポートされていないため、これを利用して他のコマンドに渡すことができます。

以下zshと同じ:

new_array=("${(@u}array}")

最近のGNUシステムでは、次のようになります。

eval "new_array=($(
  printf "%s\0" "${array[@]}" |
    LC_ALL=C sort -zu |
    xargs -r0 bash -c 'printf "%q\n" "$@"' sh
  ))"

あるいは、最新バージョンを使用し、bashすべての配列要素が空でないと仮定すると、連想配列を使用できます。

unset hash
typeset -A hash
for i in "${array[@]}"; do
  hash[$i]=
done
new_array=("${!hash[@]}")

bash 4.4以降とGNUの使用sort

readarray -td '' new_array < <(
  printf '%s\0' "${array[@]}" | LC_ALL=C sort -zu)

これらのさまざまなソリューションでは、要素の順序は同じではありません。

そしてtcsh

set -f new_array = ($array:q)

守るだろうF最初の要素(a b a=>)は拡張フラグa bのように動作します。zsh(u)

set -l new_array = ($array:q)

最後の項目(a b a=> b a)が保持されます。ただし、配列から空の要素を削除します。

答え4

全体をシェルで実行し、結果を配列に保存するには、

declare -A seen
for word in one two three two one
do
        if [ ! "${seen[$word]}" ]
        then
                result+=("$word")
                seen[$word]=1
        fi
done
echo "${result[@]}"

つまり、与えられた単語がまだ表示されていない場合は、その単語を配列resultに追加して見たものとしてマークします。単語が表示されたら、後に続く単語を無視してください。

関連情報