Comment puis-je obtenir des valeurs uniques à partir 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 de ce tableau. Ce serait simple avec sort | uniq ou sort -u comme ils l'ont mentionné dans l'autre question, mais rien n'a changé dans le tableau... Le code est:

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

Qu'est-ce que je fais de mal?

56
demandé sur Community 2012-11-30 19:43:58

11 réponses

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 assignation de tableau :

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

si votre shell supporte hérestrings ( bash devrait), vous pouvez épargner un echo processus en l'altérant à:

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

entrée:

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 de echo ou d'un hérestring. Le @ signifie "tous les éléments du tableau"
  • tr ' ' '\n' - Convertissez tous les espaces en lignes. Parce que votre tableau est vu par shell comme des éléments sur une seule ligne, séparés par et parce que tri s'attend à ce que les entrées soient sur des lignes séparées.
  • sort -u - trier et conserver uniquement les éléments uniques
  • tr '\n' ' ' - convertissez les nouvelles lignes que nous avons ajoutées plus tôt retour aux espaces.
  • $(...) - Commande De Remplacement
  • mis à part: tr ' ' '\n' <<< "${ids[@]}" est une façon plus efficace de faire: echo "${ids[@]}" | tr ' ' '\n'
85
répondu sampson-chen 2016-12-20 16:29:02

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 en bash en créant un nouveau tableau associatif qui contient chacune des valeurs du tableau original. Quelque chose comme ceci:

$ 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 parce que dans un tableau, chaque clé ne peut apparaître qu'une seule fois. Lorsque la boucle for arrive à la seconde valeur de aa dans a[2] , elle écrira b[aa] " qui a été fixé à l'origine pour a[0] .

Faire les choses en natif bash peut être plus rapide que d'utiliser des tuyaux et des outils externes comme sort et uniq .

si vous vous sentez en confiance, vous pouvez éviter la boucle for en utilisant la capacité de printf de recycler son format pour les arguments multiples, bien que cela semble exiger eval . (Arrêtez de lire maintenant si vous êtes d'accord avec ça.)

$ 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 des tableaux sont déterminées avant la séparation des mots. Cela signifie que la sortie de la substitution de commande est considérée un seul mot plutôt qu'un ensemble de clés=paires de valeurs.

bien que ceci utilise un sous-puits, il utilise seulement les builtins de bash pour traiter les valeurs du tableau. Assurez-vous d'évaluer votre utilisation de eval avec un œil critique. Si vous n'êtes pas sûr à 100% que chepner ou glenn jackman ou greycat ne trouverait aucun défaut avec votre code, utilisez la boucle for à la place.

21
répondu ghoti 2018-10-04 01:24:18

si vos éléments de tableau ont un espace blanc ou tout autre caractère spécial shell (et pouvez-vous être sûr qu'ils ne le font pas?) puis saisir les premiers de tous (et vous pouvez toujours le faire) exprimer votre tableau de guillemets! par exemple "${a[@]}" . Bash interprétera littéralement ceci comme "chaque élément de tableau dans un argument ". Au sein de bash, cela fonctionne toujours, toujours.

alors, pour obtenir un tableau trié (et unique), nous devons convertir il à une sorte de format comprend et être en mesure de le convertir de nouveau en éléments de tableau de 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 (parce que printf avait 0 arguments mais imprime encore comme s'il avait un argument vide - voir explication). Donc tu dois l'attraper dans un si ou quelque chose comme ça.

explication: Le format %q pour printf "shell échappe" l'argument imprimé, de la même manière que bash peut récupérer dans quelque chose comme eval! Parce que chaque élément est une coquille imprimée échappée sur sa propre ligne, le seul séparateur entre les éléments est la nouvelle ligne, et l'affectation de tableau prend chaque ligne comme un élément, parsant les valeurs échappées dans le texte littéral.

p.ex.

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

l'éval est nécessaire pour dépouiller l'échappée de chaque valeur retournant dans le tableau.

10
répondu vontrapp 2013-07-20 04:16:59

je me rends compte que cela a déjà été répondu, mais il s'est montré assez élevé dans les résultats de recherche, et il 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
~>
8
répondu das.cyklone 2013-07-10 05:12:47

"trier" peut être utilisé pour commander 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 juste écraser votre tableau avec les éléments uniques:

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
6
répondu corbyn42 2015-09-14 15:02:48

celui-ci préservera aussi l'ordre:

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

et de modifier le tableau original avec les valeurs uniques:

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a["151910920"]++'))
2
répondu faustus 2015-06-24 22:51:29

pour créer un nouveau tableau composé de valeurs uniques, assurez-vous que votre tableau n'est pas vide, alors faites l'une des actions 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 tri)

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

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

2
répondu Six 2015-07-31 02:05:59

chat nombre.txt

1 2 3 4 4 3 2 5 6

Imprimer la ligne dans la colonne: cat number.txt | awk 'BEGIN{FS=" "} {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 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk 'x["151960920"]++'

4
3
2

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

1
2
3
4
5
6

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

1
5
6
1
répondu VIPIN KUMAR 2016-10-06 12:54:29

si vous voulez une solution qui n'utilise que les internes de bash, vous pouvez définir les valeurs comme des 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

cette sortie

bar
foo
bar none
1
répondu rln 2017-01-11 14:42:09

sans perdre la commande initiale:

uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u["151900920"]++' | tr '\n' ' '))
0
répondu estani 2015-05-21 15:10:02

essayez ceci pour obtenir des valeurs uniq pour la première colonne dans le fichier

awk -F, '{a[];}END{for (i in a)print i;}'
0
répondu Suresh Aitha 2016-10-24 09:10:38