2009-12-09 5 views
7

J'ai une variable qui contient les entrées séparées par des espaces suivants.Suppression de doublons sur une variable sans tri

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 

Comment supprimer les doublons sans tri?

#Something like this. 
new_variable="apple lemon papaya avocado grapes mango banana" 

J'ai trouvé quelque part un script qui supprime les doublons d'une variable, mais qui trie le contenu.

#Not something like this. 
new_variable=$(echo "$variable"|tr " " "\n"|sort|uniq|tr "\n" " ") 
echo $new_variable 
apple avocado banana grapes lemon mango papaya 

Répondre

19
new_variable=$(awk 'BEGIN{RS=ORS=" "}!a[$0]++' <<<$variable); 

Voilà comment cela fonctionne:

RS (entrée enregistrement séparateur) est réglé sur un espace blanc afin qu'il traite chaque fruit dans la variable $ comme un enregistrement au lieu d'un champ. La magie unique sans tri se produit avec! A [$ 0] ++. Puisque awk supporte les tableaux associatifs, il utilise l'enregistrement courant ($ 0) comme clé du tableau a []. Si cette clé n'a pas été vue auparavant, un [$ 0] évalue à '0' (la valeur par défaut d'awk pour les index unset) qui est ensuite annulée pour renvoyer TRUE. J'exploite alors le fait que awk va par défaut 'imprimer $ 0' si une expression retourne VRAI et aucune '{commande}' n'est donnée. Enfin, un [$ 0] est alors incrémenté de sorte que cette clé ne peut plus retourner TRUE et ainsi les valeurs de répétition ne sont jamais imprimées. ORS (Output Record Separator, séparateur d'enregistrement de sortie) est également défini pour imiter le format d'entrée.

Une version moins laconique de cette commande qui produit la même sortie serait la suivante:

awk 'BEGIN{RS=ORS=" "}{ if (a[$0] == 0){ a[$0] += 1; print $0}}' 

Gotta love awk =)

EDIT

Si vous avez besoin de faire en pur Bash 2.1+, je suggère ceci:

#!/bin/bash  

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 
temp="$variable" 

new_variable="${temp%% *}" 

while [[ "$temp" != ${new_variable##* } ]]; do 
    temp=${temp//${temp%% *} /} 
    new_variable="$new_variable ${temp%% *}" 
done 

echo $new_variable; 
+0

Si vous modifiez cette option pour ajouter quelques explications que je vais vous donner un +1 – Nifle

+0

+1 comme promis. – Nifle

+0

Sweet :) Merci pour l'explication. – jhwist

1

shell

declare -a arr 
variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 
set -- $variable 
count=0 
for c in [email protected] 
do 
    flag=0 
    for((i=0;i<=${#arr[@]}-1;i++)) 
    do 
     if [ "${arr[$i]}" == "$c" ] ;then 
      flag=1 
      break 
     fi 
    done 
    if [ "$flag" -eq 0 ] ; then 
     arr[$count]="$c" 
     count=$((count+1)) 
    fi 
done 
for((i=0;i<=${#arr[@]}-1;i++)) 
do 
    echo "result: ${arr[$i]}" 
done 

Résultat lorsqu'il est exécuté:

linux# ./myscript.sh 
result: apple 
result: lemon 
result: papaya 
result: avocado 
result: grapes 
result: mango 
result: banana 

OU si vous souhaitez utiliser reluquer

awk 'BEGIN{RS=ORS=" "} (!($0 in a)){a[$0];print}' 
4

Ce pipeline la version fonctionne en préservant l'ordre d'origine:

variable=$(echo "$variable" | tr ' ' '\n' | nl | sort -u -k2 | sort -n | cut -f2-) 
3

Bash pur:

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 

declare new_value='' 

for item in $variable; do 
    if [[ ! $new_value =~ $item ]] ; then # first time? 
    new_value="$new_value $item" 
    fi 
done 
new_value=${new_value:1}     # remove leading blank 
+0

Bonne solution, mais notez que cela vous verrouille dans Bash 3.X en raison de l'opérateur '= ~'. – SiegeX

+0

Très bien. Pourquoi devrais-je changer quelque chose? C'était ton erreur. –

1

Z Shell:

% variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 
% print ${(zu)variable}                
apple lemon papaya avocado grapes mango banana 
3

En pur, portable sh:

 
words="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 
seen= 
for word in $words; do 
    case $seen in 
    $word\ * | *\ $word | *\ $word\ * | $word) 
     # already seen 
     ;; 
    *) 
     seen="$seen $word" 
     ;; 
    esac 
done 
echo $seen 
0

Une autre solution awk:

#!/bin/bash 
variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" 
variable=$(printf '%s\n' "$variable" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}') 
variable="${variable%,*}" 
echo "$variable" 

sortie:

apple lemon papaya avocado grapes mango banana 
0

solution Perl:

perl -le 'for (@ARGV){ $h{$_}++ }; for (keys %h){ print $_ }' $variable

@ARGV est la liste de paramètres d'entrée à partir de $variable
Boucle dans la liste, le remplissage de la table de hachage h avec la boucle variable $_
boucle à travers les clés du hachage h et imprimer chacun

grapes 
avocado 
apple 
lemon 
banana 
mango 
papaya 

Cette variation imprime la sortie d'abord trié par fréquence $h{$a} <=> $h{$b} puis par ordre alphabétique $a cmp $b

perl -le 'for (@ARGV){ $h{$_}++ }; for (sort { $h{$a} <=> $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" }' $variable

1  banana 
1  grapes 
1  mango 
2  apple 
2  avocado 
2  lemon 
2  papaya 

Cette variante produit la même sortie que la dernière.
Cependant, au lieu d'une coquille d'entrée variable, utilise un fichier d'entrée « fruits », avec un fruit par ligne:

perl -lne '$h{$_}++; END{ for (sort { $h{$a} <=> $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" } }' fruits

Questions connexes