Generate permutations from multiple fields (not using Python) - permutation

I'm trying to do this simply, using regular Unix functions (that I understand), and not Python (I think itertools could possibly get me there ?) which I don't understand. So: several small lists of inputs (separated by commas), which need to be used to generate all possible permutations. Example:
List 1: (I, You, We)
List 2: (want to eat, want to buy, want to steal)
List 3: (a banana, a hamburger, an icecream)
So there will be 3 X 3 X 3 = 27 possible outputs, which are:
I want to eat a banana
I want to eat a hamburger
...
and so on
I'm pretty sure there is no one liner that will do this, but is there a simple way to achieve it using simple unix building blocks ?

While not technically shell commands, a Bash script can do this pretty easily with just nested for loops.
#!/bin/bash
list1=("I" "You" "We")
list2=("want to eat" "want to buy" "want to steal")
list3=("a banana" "a hamburger" "an icecream")
for i in "${list1[#]}";
do
for j in "${list2[#]}";
do
for k in "${list3[#]}";
do
echo $i" "$j" "$k
done
done
done

Related

Practical use of bash array

After reading up on how to initialize arrays in Bash, and seeing some basic examples put forward in blogs, there remains some uncertainties on its practical use. An interesting example perhaps would be to sort in ascending order -- list countries from A to Z in random order, one for each letter.
But in the real world, how is a Bash array applied? What is it applied to? What is the common use case for arrays? This is one area I am hoping to be familiar with. Any champions in the use of bash arrays? Please provide your example.
There are a few cases where I like to use arrays in Bash.
When I need to store a collections of strings that may contain spaces or $IFS characters.
declare -a MYARRAY=(
"This is a sentence."
"I like turtles."
"This is a test."
)
for item in "${MYARRAY[#]}"; do
echo "$item" $(echo "$item" | wc -w) words.
done
This is a sentence. 4 words.
I like turtles. 3 words.
This is a test. 4 words.
When I want to store key/value pairs, for example, short names mapped to long descriptions.
declare -A NEWARRAY=(
["sentence"]="This is a sentence."
["turtles"]="I like turtles."
["test"]="This is a test."
)
echo ${NEWARRAY["turtles"]}
echo ${NEWARRAY["test"]}
I like turtles.
This is a test.
Even if we're just storing single "word" items or numbers, arrays make it easy to count and slice our data.
# Count items in array.
$ echo "${#MYARRAY[#]}"
3
# Show indexes of array.
$ echo "${!MYARRAY[#]}"
0 1 2
# Show indexes/keys of associative array.
$ echo "${!NEWARRAY[#]}"
turtles test sentence
# Show only the second through third elements in the array.
$ echo "${MYARRAY[#]:1:2}"
I like turtles. This is a test.
Read more about Bash arrays here. Note that only Bash 4.0+ supports every operation I've listed (associative arrays, for example), but the link shows which versions introduced what.

In bash, is it possible to put several arrays inside quotes and then access them?

I know I can do this:
set=("1 2 3" "4 5 6")
for subset in "${set[#]}"
do
for element in $subset
do
echo $element
done
done
1 2 3 4 5 6 will be printed sequentially. However, I can not do this:
set="(1 2 3) (4 5 6)"
for subset in $set
do
echo ${subset[2]}
done
I want to print 3 6. The reason why I want to do this is that I want to have access to whichever element I want to access during iteration instead of iterating one by one. That's why I try to put arrays inside quotes instead of putting quotes inside a big array. Is there any way to do this?
Thanks,
Unfortunately, I don't think bash supports multi-dimentional arrays, which sounds like what you're looking for. You can simulate it with a little help from bash itself like so:
x=()
x+=("1,2,3")
x+=("4,5,6")
for val in ${x[#]}; do
subset=($(echo $val | tr ',' ' '))
echo ${subset[2]}
done

Perl: Indexing function returning array syntax

I have a question about Perl more out of curiosity than necessity. I have seen there are many ways to do a lot of things in Perl, a lot of the time the syntax seems unintuitive to me (I've seen a few one liners doing som impressive stuff).
So.. I know the function split returns an array. My question is, how do I go about printing the first element of this array without saving it into a special variable? Something like $(split(" ",$_))[0] ... but one that works.
You're 99% there
$ perl -de0
Loading DB routines from perl5db.pl version 1.33
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(-e:1): 0
DB<1> $a = "This is a test"
DB<2> $b = (split(" ",$a))[0]
DB<3> p $b
This
DB<4> p "'$b'"
'This'
This should do it:
print ((split(" ", $_))[0]);
You need one set of parentheses to allow you to apply array indexing to the result of a function. The outer parentheses are needed to get around special parsing of print arguments.
Try this out to print the first element of a whitespace separated list. The \s+ regex matches one or more whitespace characters to split on.
echo "1 2 3 4" | perl -pe 'print +(split(/\s+/, $_))[0]'
Also, see this related post.

Maximum element and its indices from an array in shell script

How can I find the maximum element and its index from an array in shell script. I have an array
a = [-2.2116565098 -2.1238242060 -2.1747941240 -2.3201010162 -2.3677779871 -1.8126464132 -2.1247209755 -2.1190930712 -2.3242384636 -2.1081702064];
Now, I want to find the maximum as well as its index in bash script. Is there a shortcut like in Matlab we have
[C, I] = max(a);
Also, also how can we have multi-dimensional array and get the index and value of minimum and maximum element.
$ x='-2.2116565098 -2.1238242060 -2.1747941240 -2.3201010162 -2.3677779871'
$ IC=(`tr ' ' '\n' <<<$x | cat -n | sort -k2,2nr | head -n1`)
$ I=${IC[0]} C=${IC[1]}
$ echo $I $C
2 -2.1238242060
Shell scripts in general do not support arrays at all, so what you are asking for is impossible. I am not aware of any shells that support multi-dimensional arrays, but some shells do provide minimal support for one dimensional arrays. Some of those shells probably provide convenient ways to perform the operations you need. To find the maximum value and the index in bash, which is one particular shell that does provide primitive support for arrays, you will need to loop over the array (as far as I know). However, bash does not provide good support for floating point values, so before you implement this, you should consider using a different language. Here is an example of one method:
idx=0
maxidx=0
max=${a[0]}
for v in ${a[#]}; do
expr $v \> $max > /dev/null && { maxidx=$idx; max=$v; }
: $((idx++))
done
There may be better techniques within bash for accessing the array, but it is generally a bad idea IMO to use shell specific constructs. If you are going to be using sh, even arrays really ought to be avoided, because not all shells support them. If you want to use a language feature of a non-standard shell, you might as well use perl, python, ruby, or your language of choice.

What is a good equivalent to Perl lists in bash?

In perl one would simply do the following to store and iterate over a list of names
my #fruit = (apple, orange, kiwi);
foreach (#fruit) {
print $_;
}
What would the equivalent be in bash?
bash (unlike POSIX sh) supports arrays:
fruits=(apple orange kiwi "dried mango")
for fruit in "${fruits[#]}"; do
echo "${fruit}"
done
This has the advantage that array elements may contain spaces or other members of $IFS; as long as they were correctly inserted as separate elements, they are read out the same way.
Like this:
FRUITS="apple orange kiwi"
for FRUIT in $FRUITS; do
echo $FRUIT
done
Notice this won't work if there are spaces in the names of your fruits. In that case, see this answer instead, which is slightly less portable but much more robust.
Now that the answer I like has been accepted as the correct answer, I'll now move into another topic: how to use IFS for personal gain. :-P
fruits="apple,orange,kiwifruit,dried mango"
(IFS=,
for fruit in $fruits; do
echo "$fruit"
done)
I've put the code in brackets so that the IFS change is isolated into its own subprocess; thus at the end of the bracketed section, IFS is reverted back to its old value. :-)
for i in apple orange kiwi
do
echo $i
done

Resources