6

I define a list in bash like this:

list="element1 element_2 my_element_3 element04"

and I want to do a loop where I iterate through all possible pair combinations. In Perl, I would use a while/foreach with a shift on the list like this:

while (my $element1 = shift (@list)) {
  foreach my $element2 (@list) {
      print "$element1 - $element2\n";
  }
}

I don't want the same element in the pair and don't care about the order of the pairs, so if the list is "A B C", the result should be:

A - B
A - C
B - C

How can I do the equivalent in bash?

3 Answers 3

10

The simplest approach seems to be to use positional parameters.

set -- value1 value2 "value with spaces"
for a; do
    shift
    for b; do
        printf "%s - %s\n" "$a" "$b"
    done
done
3
  • hmmmm, just to clarify, I don't want the same element in the pair and don't care about the order of the pairs, I've edited my question now.
    – 719016
    Commented Aug 2, 2011 at 13:12
  • I just saw the shift in your original question as well. Updated answer to match.
    – jw013
    Commented Aug 2, 2011 at 13:21
  • This for loop looked odd to me. In case others are wondering, like I was (and I've just had to look it up), if no ...in <values> is specified, the loop processes each positional parameter that is set ($1, $2, $3 etc). I mean, it seems obvious now, but there you go. Thank you @jw013 for showing me this feature!
    – qmacro
    Commented Dec 3, 2020 at 8:55
2

Another way to think about your problem is: if you had (i,j) indexes for each item in your list, you would only be interested in the set of items above (or below) the diagonal (ie, where i>j).

If you did want all combinations, then you only have to test where i!=j.

I wanted to do a similar thing: generate all possible orderings of 4 items. For loops nested to a depth of 4 would not be very pretty, and an elegant conditional didn't come to mind, but this solution did:

$ for combo in  {en,fa,sp,ru}{en,fa,sp,ru}{en,fa,sp,ru}{en,fa,sp,ru}; do echo $combo; done | egrep -v 'en.*en|fa.*fa|sp.*sp|ru.*ru' 
enfaspru
enfarusp
enspfaru
ensprufa
enrufasp
enruspfa
faenspru
faenrusp
faspenru
faspruen
faruensp
faruspen
spenfaru
spenrufa
spfaenru
spfaruen
spruenfa
sprufaen
ruenfasp
ruenspfa
rufaensp
rufaspen
ruspenfa
ruspfaen
-2

dump the output to standard out, pipe to sort -u

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .