40

Python has a "set" type which contains unique objects. Does Bash have something equivalent?

I want to keep adding elements to such a bash "set" and never have duplicates.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
McBear Holden
  • 5,741
  • 7
  • 33
  • 55
  • 2
    I don't think bash provide such a data structure. In the past, I stored my strings into a file, then sort | uniq – Hai Vu Aug 17 '11 at 21:46
  • 2
    you can build such a creature as functions (if_in_set(item,arr), add_setItem(item,arr), (del_setItem(item,arr) ), but you're basically doing a for loop on an array, and if you find the key already used, then you reject the add, else you add the new item. Depending on your needs, this is OK. If your need is a one time, and or urgent, then `mySet="$(sort -u tmpFile)"` maybe good enough. Good luck. – shellter Aug 17 '11 at 21:55

3 Answers3

21

Bash 4.0 has associative arrays, which can be used to build sets.

Here's an article that discusses them (it was the first Google hit for "bash associative arrays").

(Personally, I'd just use something other than bash, probably Perl.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
17
some_dups=(aa aa b b c)
uniques=($(for v in "${some_dups[@]}"; do echo "$v";done| sort| uniq| xargs))
echo "${uniques[@]}"

gives

aa b c

also in bash 3, where no associative arrays are available

vak
  • 1,694
  • 14
  • 18
  • 3
    Thanks! You can also simplify: `uniques=($(for v in "${some_dups[@]}"; do echo "$v";done| sort -u))` – Marboni Aug 05 '16 at 13:46
14

After some googling I found a nice bash implementation at http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/. It has all the usual set operators in multiple ways, and even a printable pdf cheatsheet.

I've used associative arrays though, it's much more readable.

Declare a setA associative array variable:

$ declare -A setA

Or declare and add the initial members at the same time:

$ declare -A setA=([memberA]=1 [memberB]=1)

Add a member to the set:

$ setA[memberC]=1

Test for membership:

$ [ -n "${setA[memberC]}" ] && echo in set || echo not in set
in set

$ [ -n "${setA[memberD]}" ] && echo in set || echo not in set
not in set

List members (space separated):

$ echo "${!setA[@]}"
memberA memberC memberB

or (newline separated):

$ printf '%s\n' "${!setA[@]}"
memberB
memberC
memberA

Iterate through members:

$ for m in "${!setA[@]}"; do echo "$m"; done
memberB
memberC
memberA

Cardinality (number of members in the set):

$ echo ${#setA[@]}
3

Remove a member:

$ unset setA[memberC]

Use quotes to add members with spaces in their name:

$ setA["member with space"]=1

And it's possible to use variables as members too:

$ read -r str
$ setA["$str"]=1
Paul Tobias
  • 1,962
  • 18
  • 18