Usuń wszystkie zduplikowane słowa z łańcucha za pomocą skryptu powłoki

12

Mam taki ciąg

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

Chcę usunąć zduplikowane słowo z ciągu, a następnie wynik będzie podobny

"aaa,bbb,ccc"

Próbowałem tego kodu źródłowego

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

Działa dobrze z tą samą wartością, ale kiedy podam moją wartość zmiennej, pokazuje także wszystkie zduplikowane słowa.

Jak mogę usunąć zduplikowaną wartość.

AKTUALIZACJA

Moje pytanie polega na dodaniu całej odpowiadającej wartości do jednego ciągu, jeśli użytkownik jest taki sam. Mam takie dane ->

   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

W kodowaniu pobieram wszystkich różnych użytkowników, a następnie z powodzeniem łączę ciąg kolorów. Dla tego, że używam kodu -

while read the records 

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

Kiedy drukuję tę zmienną $ c, otrzymuję wynik (dla użytkownika AAA)

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

Chcę usunąć zduplikowany kolor. Następnie pożądane wyjście powinno być jak

"red,black,blue,green"

Do tego pożądanego wyjścia użyłem powyższego kodu

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

ale wyświetla dane wyjściowe ze zduplikowanymi wartościami

„czerwony, czarny, niebieski, czerwony, zielony, czerwony, czarny, niebieski, czerwony, zielony”, Dzięki

Urvashi
źródło
3
Wyjaśnij, co jest nie tak z tym, czego używasz. Nie rozumiem, co rozumiesz przez „kiedy podaję swoją zmienną wartość”. Jaką wartość dajesz? Gdzie to zawodzi?
terdon
echo 'aaa aaa aaa bbb bbb ccc bbb ccc' | xargs -n1 | sort -u | xargsdaje aaa bbb ccc.. więc musisz pokazać dokładny kod, który zmęczyłeś i s='aaa aaa aaa bbb bbb ccc bbb ccc'; echo "$s" | xargs -n1 | sort -u | xargs
wypisać,
wartość ciągu przychodzi dynamicznie. Drukuje tę samą wartość (zawiera zduplikowaną wartość).
Urvashi
1
tak, pokaż kod, który zawiódł, w przeciwnym razie skąd mielibyśmy wiedzieć, co mogło pójść nie tak?
Sundeep
Czy kolejność ma znaczenie?
Jacob Vlijm

Odpowiedzi:

12

Jeszcze jeden awk, dla zabawy:

$ 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 

Nawiasem mówiąc, nawet twoje rozwiązanie działa dobrze ze zmiennymi:

$ b="zebra ant spider spider ant zebra ant" 
$ echo "$b" | xargs -n1 | sort -u | xargs
ant spider zebra
George Vasiliou
źródło
Schludne podejście. Jedyne, co musiałem zrobić, to użyć %szamiast %s%s. Powodem jest to, że robiłem pętlę for przez wyniki, a dwie białe spacje powodowały pewne wyzwania z dopasowaniami regularnymi.
JeremyCanfield,
9

Z tr, sortiuniq

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

lub

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

dostać jedną linię

Michael D.
źródło
Musisz dodać, | xargsaby ponownie dołączyć dane wyjściowe do jednej linii
Philippos
4
Lub użyj sort -u. Lub nawet awk '!u[$0]++.
Benoît
2
@ Benoît Wow, nie wiedziałem o sort -u. Używam przez sort | uniqcały ten czas. Zmarnowane naciśnięcia klawiszy ...
ogrodnik
8
$ echo "zebra ant spider spider ant zebra ant"  | awk -v RS="[ \n]+" '!n[$0]++' 
zebra
ant
spider
JJoao
źródło
1
Bardzo mądry!!!!
George Vasiliou
@GeorgeVasiliou, dziękuję [lub prawdę mówiąc, bardzo leniwy :-)]
JJoao
2

Z GNU sed:

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

Możesz dodać, ;s/ */ /gaby usunąć zdublowane spacje.

Funkcje takie jak to: Jeśli słowo jest po raz drugi w tym wierszu, usuń je i zacznij od nowa, aż nie będzie już żadnej publikacji.

Philippos
źródło
Jakie są \<i \>?
someonewithpc
@someonewithpc Nie pasują do żadnego znaku, ale na początku i na końcu słowa, aby zapobiec dopasowaniu podciągów.
Philippos
Fajnie, ale czy to jest przenośne? Czy słowa nie są oddzielone spacjami? Wydaje się, że nadmiarowe nie jest dopasowanie do białych znaków, po których następuje koniec słowa.
someonewithpc
1
@someonewithpc Nie, to nie jest standardowe, dlatego napisałem gnu sed . Zaletą jest to, że nie musisz osobno obsługiwać pierwszego i ostatniego łańcucha
Philippos
2
perl -lane '$,=$";print grep { ! $h{$_}++ } @F'

źródło
2

Obowiązkowe rozwiązanie awk:

$ 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 

(Finał echojest dostępny dla nowej linii)

ilkkachu
źródło
Plus jeden za awk! Budowałem też rozwiązanie awk dla zabawy. Istnieje niewielka możliwość wydrukowania słów w kolejności losowej w sekcji END ze względu na losowy sposób, w jaki awk iteruje w klawiszach tablicy.
George Vasiliou
Tak, zostaną wydrukowane w zasadniczo losowej kolejności. sortRozwiązanie nie zachować oryginalną kolejność albo, choć.
ilkkachu
Tak, dobra uwaga! Nawet sortuj wydruki w innej kolejności niż na wejściu.
George Vasiliou
1
@ilkkachu Właściwie nie musimy czekać na zakończenie wprowadzania. Możemy podjąć decyzję o drukowaniu lub nie drukować z niewielką modyfikacją kodu: awk -vRS=" " -vORS=" " '!a[$1]++ {print $1}' ; echoTo zachowuje zamówienie.
1

Pyton

opcja 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)

Ustaw plik wykonywalny, a następnie zadzwoń z Bash:

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

Możesz też zaimplementować go jako funkcję Bash, ale składnia jest nieporządna.

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

Opcja 2

W razie potrzeby ta opcja może stać się jednostronną:

#!/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

W Bash:

get_unique_words(){
  python -c "print ','.join(set('$1'.split(',')))"
}
wjandrea
źródło
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
天津 神 こ と
źródło
Nie rozumiem
Pierre.Vriens
1
Twój kod nie ma wyjaśnienia. Bez wyjaśnienia trudno jest śledzić, co się dzieje. Wydaje się również, że przyjmujesz założenia dotyczące danych, które wydają się niepoprawne (pola rozdzielane białymi znakami) oraz o konkretnej awkzastosowanej implementacji ( asorti()nie jest to awkfunkcja standardowa ).
Kusalananda
0

Korzystanie z oryginalnych danych tabelarycznych w pliku o nazwie file:

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

To generuje

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

Trzy etapy rurociągu:

  1. sedPolecenie usuwa pierwszą linię, która jest nagłówek, że nie chcemy czytać.
  2. sortKomenda daje nam unikalne linie. Przykładowe dane po sortwyglądzie

    AAA         | black
    AAA         | blue
    AAA         | green
    AAA         | red
    BBB         | blue
    BBB         | red
    CCC         | red
  3. awkKomenda bierze te dane i tworzy ciąg oddzielonych przecinkami dla każdego użytkownika w tablicy color(gdzie nazwa użytkownika jest kluczem do tablicy). Na końcu (w ENDbloku) wszystkie zebrane dane są wyprowadzane.
Kusalananda
źródło
-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
źródło
Dodaj wyjaśnienie, jak działa Twój kod i dlaczego to zrobiłeś.
xhienne