Je me rends compte que cela a déjà été répondu, mais il est apparu assez haut dans les résultats de recherche, et cela pourrait aider quelqu'un.
printf "%s\n" "${IDS[@]}" | sort -u
Exemple :
~> 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
~>
Si vos éléments de tableau ont des espaces blancs ou tout autre caractère spécial du shell (et pouvez-vous être sûr qu'ils n'en ont pas ?), alors pour les capturer tout d'abord (et vous devriez toujours le faire), exprimez votre tableau entre guillemets doubles ! par exemple. "${a[@]}"
. Bash interprétera littéralement cela comme "chaque élément de tableau dans un argument séparé ". Dans bash, cela fonctionne toujours, toujours.
Ensuite, pour obtenir un tableau trié (et unique), nous devons le convertir dans un format que le tri comprend et pouvoir le reconvertir en éléments de tableau bash. C'est le meilleur que j'ai trouvé :
eval a=($(printf "%q\n" "${a[@]}" | sort -u))
Malheureusement, cela échoue dans le cas particulier du tableau vide, transformant le tableau vide en un tableau de 1 élément vide (car printf avait 0 argument mais s'imprime toujours comme s'il avait un argument vide - voir l'explication). Vous devez donc saisir cela dans un si ou quelque chose.
Explication :Le format %q pour printf "shell échappe" à l'argument imprimé, de la même manière que bash peut récupérer quelque chose comme eval ! Parce que chaque élément est imprimé avec échappement shell sur sa propre ligne, le seul séparateur entre les éléments est la nouvelle ligne , et l'affectation du tableau prend chaque ligne comme un élément, analysant les valeurs échappées en texte littéral.
ex.
> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''
L'eval est nécessaire pour supprimer l'échappement de chaque valeur retournant dans le tableau.
Si vous utilisez Bash version 4 ou supérieure (ce qui devrait être le cas dans n'importe quelle version moderne de Linux), vous pouvez obtenir des valeurs de tableau uniques dans bash en créant un nouveau tableau associatif contenant chacune des valeurs du tableau d'origine. Quelque chose comme ça :
$ 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
Cela fonctionne car dans n'importe quel tableau (associatif ou traditionnel, dans n'importe quelle langue), chaque clé ne peut apparaître qu'une seule fois. Lorsque le for
la boucle arrive à la seconde valeur de aa
en a[2]
, il écrase b[aa]
qui a été défini à l'origine pour a[0]
.
Faire des choses dans bash natif peut être plus rapide que d'utiliser des canaux et des outils externes comme sort
et uniq
, bien que pour les ensembles de données plus volumineux, vous obtiendrez probablement de meilleures performances si vous utilisez un langage plus puissant comme awk, python, etc.
Si vous vous sentez en confiance, vous pouvez éviter le for
boucle en utilisant printf
la capacité de recycler son format pour plusieurs arguments, bien que cela semble nécessiter eval
. (Arrêtez de lire maintenant si cela vous convient.)
$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )
La raison pour laquelle cette solution nécessite eval
est que les valeurs du tableau sont déterminées avant le fractionnement des mots. Cela signifie que la sortie de la substitution de commande est considérée comme un seul mot plutôt qu'un ensemble de paires clé=valeur.
Bien que cela utilise un sous-shell, il utilise uniquement les commandes intégrées bash pour traiter les valeurs du tableau. Assurez-vous d'évaluer votre utilisation de eval
d'un œil critique. Si vous n'êtes pas sûr à 100 % que chepner ou glenn jackman ou greycat ne trouveront rien à redire à votre code, utilisez plutôt la boucle for.
Un peu hacky, mais ça devrait le faire :
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
Pour enregistrer les résultats uniques triés dans un tableau, effectuez une affectation de tableau :
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
Si votre shell prend en charge les chaînes ici (bash
devrait), vous pouvez épargner un echo
processus en le modifiant en :
tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '
Remarque en date du 28 août 2021 :
Selon ShellCheck wiki 2207 un read -a
pipe doit être utilisé pour éviter le fractionnement. Ainsi, dans bash, la commande serait :
IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"
ou
IFS=" " read -r -a ids <<< "$(tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ')"
Saisie :
ids=(aa ab aa ac aa ad)
Sortie :
aa ab ac ad
Explication :
"${ids[@]}"
- Syntaxe pour travailler avec des tableaux shell, qu'ils soient utilisés dans le cadre deecho
ou un herestring. Le@
partie signifie "tous les éléments du tableau"tr ' ' '\n'
- Convertir tous les espaces en nouvelles lignes. Parce que votre tableau est vu par shell comme des éléments sur une seule ligne, séparés par des espaces; et parce que le tri s'attend à ce que l'entrée soit sur des lignes séparées.sort -u
- trier et conserver uniquement les éléments uniquestr '\n' ' '
- reconvertir les retours à la ligne que nous avons ajoutés précédemment en espaces.$(...)
- Substitution de commandes- À part :
tr ' ' '\n' <<< "${ids[@]}"
est une façon plus efficace de faire :echo "${ids[@]}" | tr ' ' '\n'