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 ...

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

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

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.


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


@ 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.


2
Perhatikan bahwa Anda perlu unset seensebelum declare -A seendalam kasus $seensebelumnya didefinisikan (bahkan sebagai variabel skalar dari lingkungan).
Stéphane Chazelas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.