Come creare una matrice di elementi unici da una stringa / matrice in bash?


8

Se ho una stringa "1 2 3 2 1" - o un array [1,2,3,2,1] - come posso selezionare i valori univoci, ad es.

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

o

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

Simile a uniq ma uniq sembra funzionare su intere linee, non su schemi all'interno di una linea ...

Risposte:


4

Con GNU awk(questo mantiene anche l'ordine originale)

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

In readun 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

Come posso quindi trasformarlo in un array?
Michael Durrant,

@MichaelDurrant, se intendi un basharray, ha aggiunto un modo
iruvar il

Vedi qui se il tuo array contiene spazi bianchi
Tom Hale,

@iruvar puoi per favore spiegare cosa significa in realtà? sono nuovo nello scripting awk e sarebbe utile se puoi chiarire cosa succede realmente quando dici questo! a [$ 0] ++
Abhishek,

@iruvar se non fosse possibile spiegare nei commenti qualsiasi sito web che spieghi almeno la sintassi sopra sarebbe utile.
Abhishek,

9

Se stai usando zsh:

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

o (se l' KSH_ARRAYSopzione non è impostata) pari

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

1
Se l'array può contenere elementi vuoti, è necessario utilizzare "${(u)array[@]}"o "${(@u)array}"invece (notare le virgolette).
Stéphane Chazelas,

Sto usando zsh 5.1.1 (x86_64-ubuntu-linux-gnu) e ${(u)array}funziona anche se l'array è vuoto o contiene una stringa vuota, senza virgolette.
kiamlaluno,

4

Per un array con valori arbitrari, è piuttosto complicato bashin quanto non ha un operatore incorporato per questo.

bash tuttavia non supporta l'archiviazione di caratteri NUL nelle sue variabili, quindi è possibile utilizzarlo per passarlo ad altri comandi:

L'equivalente di zsh's:

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

su un recente sistema GNU, potrebbe essere:

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

In alternativa, con le versioni recenti di bashe supponendo che nessuno degli elementi dell'array sia vuoto, è possibile utilizzare array associativi:

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

Con bash 4.4 e versioni successive e con GNU sort:

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

L'ordine degli elementi non sarebbe lo stesso in quelle diverse soluzioni.

Con tcsh:

set -f new_array = ($array:q)

Manterrebbe la f elemento irst ( a b a=> a b) come zsh's (u)bandiera espansione.

set -l new_array = ($array:q)

Manterrebbe l'ultimo ( a b a=> b a). Quelli tuttavia rimuovono gli elementi vuoti dall'array.


1

Questa soluzione ha funzionato per me.

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

Quanto sopra produce 1 2 3 come uscita.

La versione più breve come suggerito da Costas potrebbe essere,

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

Per archiviare i risultati finali in un array, è possibile fare qualcosa del tipo,

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

Ora, quando faccio eco arr, questo è l'output che ottengo.

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

Riferimenti

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


@Costas, grazie. L'ho incorporato nella risposta.
Ramesh,

Come posso fare in modo che il risultato finale sia un array?
Michael Durrant,

@MichaelDurrant, per favore vedi la risposta aggiornata e fammi sapere se va bene.
Ramesh,

Se si desidera mettere il risultato nella matrice, è possibile rimuovere l'ultimo comandotr '\n' ' '
Costas,

0

Per farlo interamente nella shell e mettere il risultato in un 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[@]}"

In parole: se non abbiamo ancora visto una determinata parola, aggiungila alla resultmatrice e contrassegnala come se fosse stata vista. Una volta che una parola è stata vista, ignora le successive apparenze di essa.


2
Si noti che è necessario unset seenprima declare -A seennel caso in cui $seenfosse stato precedentemente definito (anche come variabile scalare dall'ambiente).
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.