Supprimer tous les mots en double de la chaîne à l'aide du script shell

12

J'ai une chaîne comme

"aaa,aaa,aaa,bbb,bbb,ccc,bbb,ccc"

Je veux supprimer le mot en double de la chaîne, puis la sortie sera comme

"aaa,bbb,ccc"

J'ai essayé ce code source

$ echo "zebra ant spider spider ant zebra ant" | xargs -n1 | sort -u | xargs

Il fonctionne très bien avec la même valeur, mais lorsque je donne ma valeur de variable, il affiche également tous les mots en double.

Comment puis-je supprimer la valeur en double.

METTRE À JOUR

Ma question est d'ajouter toutes les valeurs correspondantes dans une seule chaîne si l'utilisateur est le même. J'ai des données comme celle-ci ->

   user name    | colour
    AAA         | red
    AAA         | black
    BBB         | red
    BBB         | blue
    AAA         | blue
    AAA         | red
    CCC         | red
    CCC         | red
    AAA         | green
    AAA         | red
    AAA         | black
    BBB         | red
    BBB         | blue
    AAA         | blue
    AAA         | red
    CCC         | red
    CCC         | red
    AAA         | green

Dans le codage, je récupère tous les utilisateurs distincts, puis je concatène la chaîne de couleur avec succès. Pour que j'utilise du code -

while read the records 

    if [ "$c" == "" ]; then  #$c I defined global
        c="$colour1"
    else
        c="$c,$colour1" 
    fi

Lorsque j'imprime cette variable $ c, j'obtiens la sortie (pour l'utilisateur AAA)

"red,black,blue,red,green,red,black,blue,red,green,"

Je veux supprimer la couleur en double. La sortie souhaitée devrait alors être comme

"red,black,blue,green"

Pour cette sortie souhaitée, j'ai utilisé le code ci-dessus

 echo "zebra ant spider spider ant zebra ant" | xargs -n1 | sort -u | xargs

mais il affiche la sortie avec des valeurs en double.

"rouge, noir, bleu, rouge, vert, rouge, noir, bleu, rouge, vert", merci

Urvashi
la source
3
Veuillez clarifier ce qui ne va pas avec ce que vous utilisez. Je ne comprends pas ce que vous entendez par «quand je donne ma valeur de variable». Quelle valeur donnez-vous? Où échoue-t-il?
terdon
echo 'aaa aaa aaa bbb bbb ccc bbb ccc' | xargs -n1 | sort -u | xargsdonne aaa bbb ccc.. donc vous devez montrer le code exact que vous avez fatigué et le résultat que vous avez obtenu .. avec la chaîne en variable:s='aaa aaa aaa bbb bbb ccc bbb ccc'; echo "$s" | xargs -n1 | sort -u | xargs
Sundeep
la valeur de la chaîne est dynamique. Il imprime la même valeur (contient une valeur en double).
Urvashi
1
oui, montrez le code qui a échoué, sinon comment saurions-nous ce qui aurait pu mal tourner?
Sundeep
La commande est-elle importante?
Jacob Vlijm

Réponses:

12

Encore un awk, juste pour le plaisir:

$ a="aaa bbb aaa bbb ccc aaa ddd bbb ccc"
$ echo "$a" | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}'
aaa bbb ccc ddd 

Soit dit en passant, même votre solution fonctionne bien avec des variables:

$ b="zebra ant spider spider ant zebra ant" 
$ echo "$b" | xargs -n1 | sort -u | xargs
ant spider zebra
George Vasiliou
la source
Approche soignée. Le seul ajustement que j'ai dû faire était d'utiliser à la %splace de %s%s. La raison en est que je faisais une boucle for à travers les résultats et deux espaces blancs ont causé des problèmes avec les correspondances d'expression régulière.
JeremyCanfield
9

Avec tr, sortetuniq

echo "zebra ant spider spider ant zebra ant" | tr ' ' '\n' | sort | uniq

ou

echo "zebra ant spider spider ant zebra ant" | tr ' ' '\n' | sort | uniq | xargs 

pour obtenir une ligne

Michael D.
la source
Vous devez ajouter | xargspour joindre à nouveau la sortie à une ligne
Philippos
4
Ou utilisez sort -u. Ou même un awk '!u[$0]++.
Benoît
2
@ Benoît Wow, je ne connaissais pas sort -u. J'utilise sort | uniqtout ce temps. Les frappes
inutiles
8
$ echo "zebra ant spider spider ant zebra ant"  | awk -v RS="[ \n]+" '!n[$0]++' 
zebra
ant
spider
JJoao
la source
1
Très intelligent!!!!
George Vasiliou
@GeorgeVasiliou, merci [ou pour dire la vérité, très paresseux :-)]
JJoao
2

Avec gnu sed:

sed ':s;s/\(\<\S*\>\)\(.*\)\<\1\>/\1\2/g;ts'

Vous pouvez ajouter ;s/ */ /gpour supprimer les espaces en double.

Fonctions comme ceci: Si un mot est une deuxième fois dans cette ligne, supprimez-le et recommencez jusqu'à ce qu'aucune duplication ne soit trouvée.

Philippos
la source
Quels sont \<et \>?
someonewithpc
@someonewithpc Ils ne correspondent à aucun caractère, mais au début et à la fin d'un mot pour empêcher la correspondance des sous-chaînes.
Philippos
Bien, mais est-ce portable? De plus, les mots ne sont-ils pas séparés par des espaces? Semble redondant pour ne pas correspondre aux espaces suivis de la fin d'un mot.
someonewithpc
1
@someonewithpc Non, ce n'est pas standard, c'est pourquoi j'ai écrit gnu sed . La bonne partie est que vous n'avez pas à gérer la première et la dernière chaîne séparément
Philippos
2
perl -lane '$,=$";print grep { ! $h{$_}++ } @F'

la source
2

Solution awk obligatoire:

$ echo "ant zebra ant spider spider ant zebra ant" | 
   awk -vRS=" " -vORS=" " '!a[$1] {a[$1]++} END{ for (x in a) print x;  } ' ; echo
zebra ant spider 

(La finale echoest là pour la nouvelle ligne)

ilkkachu
la source
Plus un pour l'awk! Je construisais également une solution awk juste pour le plaisir. Il y a une légère possibilité que les mots soient imprimés dans un ordre aléatoire dans la section END en raison de la façon aléatoire qui awk itère dans les clés du tableau.
George Vasiliou
Oui, ils seront imprimés dans un ordre essentiellement aléatoire. La sortsolution ne tient pas l'ordre initial soit, cependant.
ilkkachu
Oui, bon point! Triez même les impressions dans un ordre différent de celui saisi.
George Vasiliou
1
@ilkkachu En fait, nous n'avons pas besoin d'attendre la fin de l'entrée. Nous pouvons décider d'imprimer ou de ne pas imprimer avec une légère modification de votre code: awk -vRS=" " -vORS=" " '!a[$1]++ {print $1}' ; echocela préserve la commande.
1

Python

Option 1

#!/usr/bin/env python
# get_unique_words.py

import sys

l = []
for w in sys.argv[1].split(','):
  if w not in l:
    l += [ w ]
print ','.join(l)

Rendez exécutable, puis appelez depuis Bash:

$ ./get_unique_words.py "aaa,aaa,aaa,bbb,bbb,ccc,bbb,ccc"
aaa,bbb,ccc

Ou vous pouvez l'implémenter en tant que fonction Bash, mais la syntaxe est désordonnée.

get_unique_words(){
  python -c "
l = []
for w in '$1'.split(','):
  if w not in l:
    l += [ w ]
print ','.join(l)"
}

Option 2

Cette option peut devenir monoligne si nécessaire:

#!/usr/bin/env python
# get_unique_words.py

import sys

s_in = sys.argv[1]
l_in = s_in.split(',') # Turn string into a list.
set_out = set(l_in) # Turning a list into a set removes duplicates items.
s_out = ','.join(set_out) 
print s_out

Dans Bash:

get_unique_words(){
  python -c "print ','.join(set('$1'.split(',')))"
}
wjandrea
la source
0
cat filename | awk '{ delete a; for (i=1; i<=NF; i++) a[$i]++; n=asorti(a, b); for (i=1; i<=n; i++) printf b[i]" "; print "" }' > newfile
天津 神 こ と
la source
Je ne comprends pas
Pierre.Vriens
1
Votre code manque d'explication. Sans explication, il est difficile de suivre ce qui se passe. Vous semblez également émettre des hypothèses sur les données qui semblent incorrectes (champs délimités par des espaces) et sur l' awkimplémentation particulière utilisée ( asorti()n'est pas une awkfonction standard ).
Kusalananda
0

Utilisation des données tabulaires d'origine dans le fichier appelé file:

sed '1d' file | sort -u |
awk '{ color[$1] = ( color[$1] == "" ? $3 : color[$1] "," $3 ) }
     END { for (user in color) print user, color[user] }'

Cela génère

CCC red
BBB blue,red
AAA black,blue,green,red

Les trois étapes du pipeline:

  1. La sedcommande supprime la première ligne qui est un en-tête que nous ne voulons pas lire.
  2. La sortcommande nous donne des lignes uniques. Les échantillons de données après les sortregards comme

    AAA         | black
    AAA         | blue
    AAA         | green
    AAA         | red
    BBB         | blue
    BBB         | red
    CCC         | red
  3. La awkcommande prend ces données et produit une chaîne délimitée par des virgules pour chaque utilisateur du tableau color(où le nom d'utilisateur est la clé du tableau). À la fin (dans le ENDbloc), toutes les données collectées sont sorties.
Kusalananda
la source
-2
a="aaa aaa aaa bbb bbb ccc bbb ccc"
for item in $a
do
   echo $item
done | sort -u | (while read i; do ans="$ans $i"; done ; echo $ans)
Tododo Fly
la source
Veuillez ajouter une explication sur le fonctionnement de votre code et pourquoi vous avez fait ceci et cela.
xhienne