web-dev-qa-db-fra.com

Comment puis-je obtenir des valeurs uniques d'un tableau dans Bash?

J'ai presque la même question que ici .

J'ai un tableau qui contient aa ab aa ac aa ad, etc . Maintenant, je veux sélectionner tous les éléments uniques de ce tableau . Pensé, cela serait simple avec sort | uniq ou avec sort -u comme ils l'ont mentionné dans cette autre question, mais rien n'a changé dans le tableau ... Le code est:

echo `echo "${ids[@]}" | sort | uniq`

Qu'est-ce que je fais mal?

60
Jetse

Un peu hacky, mais cela devrait le faire:

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

Pour sauvegarder les résultats uniques triés dans un tableau, faites Affectation de tableau :

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

Si votre Shell prend en charge herestrings (bash devrait), vous pouvez épargner un processus echo en le modifiant comme suit:

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

Contribution:

ids=(aa ab aa ac aa ad)

Sortie:

aa ab ac ad

Explication:

  • "${ids[@]}" - Syntaxe d'utilisation des baies Shell, qu'elle soit utilisée dans le cadre de echo ou d'un herestring. La partie @ signifie "tous les éléments du tableau"
  • tr ' ' '\n' - Convertissez 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 la sorte s'attend à ce que l'entrée soit sur des lignes séparées.
  • sort -u - ne triez et ne conservez que des éléments uniques
  • tr '\n' ' ' - convertit les nouvelles lignes que nous avons ajoutées précédemment en espaces.
  • $(...) - Commande Substitution
  • De côté: tr ' ' '\n' <<< "${ids[@]}" est un moyen plus efficace de faire: echo "${ids[@]}" | tr ' ' '\n'
93
sampson-chen

Si vous utilisez Bash version 4 ou supérieure (ce qui devrait être le cas dans toutes les versions modernes 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 un tableau, chaque clé ne peut apparaître qu'une seule fois. Lorsque la boucle for arrive à la deuxième valeur de aa dans a[2], elle remplace b[aa] qui avait été définie à l'origine pour a[0].

Faire des choses en bash natif peut être plus rapide qu'avec des pipes et des outils externes comme sort et uniq.

Si vous êtes confiant, vous pouvez éviter la boucle for en utilisant la capacité de printf à recycler son format pour plusieurs arguments, bien que cela semble nécessiter eval. (Arrêtez de lire maintenant si ça vous va.)

$ 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 de tableau sont déterminées avant le fractionnement de Word. Cela signifie que la sortie de la substitution de commande est considérée/ 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. Veillez à évaluer votre utilisation de eval avec un œil critique. Si vous n'êtes pas sûr à 100% que Chepner, Glenn Jackman ou Greycat ne trouveront aucune anomalie dans votre code, utilisez plutôt la boucle for.

22
ghoti

Si vos éléments de tableau ont des espaces ou tout autre caractère spécial du shell (et pouvez-vous être sûr qu'ils n'en ont pas?), Alors capturez-les d'abord (et vous devriez toujours le faire), exprimez votre tableau entre guillemets! par exemple. "${a[@]}". Bash interprétera littéralement ceci comme "chaque élément de tableau dans un argument séparé". Dans bash, cela fonctionne simplement, toujours.

Ensuite, pour obtenir un tableau trié (et unique), nous devons le convertir en un format que le type comprend et pouvoir le reconvertir en éléments de tableau bash. C'est le meilleur que j'ai trouvé avec:

eval a=($(printf "%q\n" "${a[@]}" | sort -u))

Malheureusement, cela échoue dans le cas particulier du tableau vide, transformant le tableau vide en tableau de 1 élément vide (car printf a 0 argument mais continue à imprimer comme s'il ne contenait qu'un argument vide - voir l'explication). Donc, vous devez attraper cela dans un if ou quelque chose.

Explication: Le format% q de printf "Shell échappe" à l’argument imprimé, de la même manière que bash peut récupérer quelque chose comme eval! Comme chaque élément est imprimé, le shell échappé se trouve sur sa propre ligne, le seul le séparateur entre les éléments est la nouvelle ligne, et l'assignation de tableau prend chaque ligne comme un élément, analysant les valeurs échappées en texte littéral.

par exemple.

> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''

Eval est nécessaire pour éliminer l'échappement de chaque valeur renvoyée dans le tableau.

11
vontrapp

Je me rends compte que la réponse à cette question a déjà été donnée, mais les résultats de recherche affichent des résultats assez élevés et peuvent 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
~>
8
das.cyklone

'sort' peut être utilisé pour ordonner la sortie d'une boucle for:

for i in ${ids[@]}; do echo $i; done | sort

et éliminer les doublons avec "-u":

for i in ${ids[@]}; do echo $i; done | sort -u

Enfin, vous pouvez simplement écraser votre tableau avec les éléments uniques:

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
7
corbyn42

celui-ci préservera également l'ordre: 

echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'

et pour modifier le tableau d'origine avec les valeurs uniques:

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))
2
faustus

Pour créer un nouveau tableau composé de valeurs uniques, assurez-vous que votre tableau n'est pas vide, puis effectuez l'une des opérations suivantes:

Supprimer les entrées en double (avec tri)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)

Supprimer les entrées en double (sans trier)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')

Avertissement: N'essayez pas de faire quelque chose comme NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) ). Il va casser sur les espaces.

2
Six

numéro de chat.txt 

1 2 3 4 4 3 2 5 6

imprimer la ligne dans la colonne: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' 

1
2
3
4
4
3
2
5
6

trouver les enregistrements en double: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++' 

4
3
2

Remplacer les enregistrements en double: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++' 

1
2
3
4
5
6

Rechercher uniquement des enregistrements Uniq: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

1
5
6
1
VIPIN KUMAR

Si vous souhaitez une solution qui utilise uniquement les composants internes bash, vous pouvez définir les valeurs en tant que clés dans un tableau associatif, puis extraire les clés:

declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do 
  uniqs["${f}"]=""
done

for thing in "${!uniqs[@]}"; do
  echo "${thing}"
done

Cela produira

bar
foo
bar none
1
rln

Essayez ceci pour obtenir des valeurs uniques pour la première colonne du fichier.

awk -F, '{a[$1];}END{for (i in a)print i;}'
0
Suresh Aitha

Sans perdre la commande d'origine:

uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))
0
estani