Bagaimana cara membuat array elemen unik dari string / array di bash?

8

Jika saya memiliki string "1 2 3 2 1" - atau sebuah array [1,2,3,2,1] - bagaimana saya bisa memilih nilai unik, yaitu

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

atau

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

Mirip dengan uniq tetapi uniq tampaknya bekerja pada seluruh baris, bukan pola dalam satu baris ...

Michael Durrant
sumber

Jawaban:

4

Dengan GNU awk(ini juga mempertahankan pesanan asli)

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

Untuk readmenjadi sebuah basharray

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
iruvar
sumber
Lalu bagaimana saya bisa membuat array itu?
Michael Durrant
@MichaelDurrant, jika Anda maksud basharray, menambahkan cara
iruvar
Lihat di sini jika array Anda mengandung spasi putih
Tom Hale
@iruvar bisa tolong jelaskan apa artinya ini sebenarnya? Saya baru mengenal awk scripting dan akan sangat membantu jika Anda dapat mengklarifikasi apa yang sebenarnya terjadi ketika Anda mengatakan ini! a [$ 0] ++
Abhishek
@iruvar jika tidak mungkin untuk menjelaskan dalam komentar situs web mana pun yang menjelaskan sintaks di atas setidaknya akan bermanfaat.
Abhishek
9

Jika Anda menggunakan zsh:

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

atau (jika KSH_ARRAYSopsi tidak disetel) genap

$ echo ${(u)array}
1 2 3
jimmij
sumber
1
Jika array mungkin mengandung elemen kosong, Anda harus menggunakan "${(u)array[@]}"atau "${(@u)array}"sebagai gantinya (perhatikan tanda kutip).
Stéphane Chazelas
Saya menggunakan zsh 5.1.1 (x86_64-ubuntu-linux-gnu) , dan ${(u)array}berfungsi bahkan jika arraynya kosong atau berisi string kosong, tanpa tanda kutip.
kiamlaluno
4

Untuk array dengan nilai arbitrer, cukup rumit bashkarena tidak memiliki operator bawaan untuk itu.

bash namun terjadi tidak mendukung penyimpanan karakter NUL dalam variabelnya, sehingga Anda dapat menggunakannya untuk meneruskannya ke perintah lain:

Setara zshdengan:

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

pada sistem GNU baru-baru ini, dapat:

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

Atau, dengan versi terbaru dari bash, dan dengan asumsi tidak ada elemen array yang kosong, Anda bisa menggunakan array asosiatif:

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

Dengan bash 4.4 dan yang lebih baru dan dengan GNU sort:

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

Urutan elemen tidak akan sama dalam solusi yang berbeda tersebut.

Dengan tcsh:

set -f new_array = ($array:q)

Akan mempertahankan f elemen irst ( a b a=> a b) seperti zsh's (u)bendera ekspansi.

set -l new_array = ($array:q)

Akan mempertahankan yang terakhir ( a b a=> b a). Namun itu menghapus elemen kosong dari array.

Stéphane Chazelas
sumber
1

Solusi ini berhasil untuk saya.

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

Di atas menghasilkan 1 2 3 sebagai output.

Versi yang lebih pendek seperti yang disarankan Costas adalah,

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

Untuk menyimpan hasil akhir ke array, Anda bisa melakukan sesuatu seperti,

IFS=$' '
arr=($(printf "%s\n" "${ids[@]}" | sort -u | tr '\n' ' '))
unset IFS

Sekarang, ketika saya melakukan gema arr, ini adalah output yang saya dapatkan.

echo "${arr[@]}"
1 2 3

Referensi

https://stackoverflow.com/a/13648438/1742825 https://stackoverflow.com/a/9449633/1742825

Ramesh
sumber
@ Costas, terima kasih. Saya telah memasukkannya ke dalam jawabannya.
Ramesh
Bagaimana saya bisa membuat hasil akhir menjadi sebuah array?
Michael Durrant
@MichaelDurrant, silakan lihat jawaban yang diperbarui dan beri tahu saya apakah ini baik-baik saja.
Ramesh
Jika Anda ingin memasukkan hasilnya ke dalam array, Anda dapat menghapus perintah terakhirtr '\n' ' '
Costas
0

Untuk melakukannya sepenuhnya di shell dan meletakkan hasilnya dalam array,

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[@]}"

Dengan kata lain: jika kita belum melihat kata yang diberikan, tambahkan ke resultarray dan beri tanda sebagai telah dilihat. Begitu sebuah kata telah terlihat, abaikan penampilan berikutnya dari itu.

Scott
sumber
2
Perhatikan bahwa Anda perlu unset seensebelum declare -A seendalam kasus $seensebelumnya didefinisikan (bahkan sebagai variabel skalar dari lingkungan).
Stéphane Chazelas