2

I want to run a loop from 1 through 12, match the current loop number against pre-defined arrays then output which array is matched.

Each type of month gets an array.

lm #has 4,6,9,11 
hm #has 1,3,5,7,8,10,12 
feb #special case, if its not an hm or lm, its feb

Here is my current code :

#!/bin/bash
lm=()
lm+=(4)
lm+=(6)
lm+=(9)
lm+=(11)

hm=()
hm+=(1)
hm+=(3)
hm+=(5)
hm+=(7)
hm+=(8)
hm+=(10)
hm+=(12)

feb=()
feb+=(2)

ld=(1 2)
hd=(1 2)
fd=(1 2)
for m in {1..12}
do
echo "current month is $m"
    if [[ " ${hm[*]} " == *"$m"* ]];
    then
        echo "high month"
    elif [[ " ${lm[*]} " == *"$m"* ]];
    then
        echo "low month"
    elif [[ " ${feb[*]} " = *"$m"* ]];
    then
        echo "feb"
    else
        echo "weird month input"
    fi
done

Its output :

$ ./old2.sh
current month is 1
high month
current month is 2
high month
current month is 3
high month
current month is 4
low month
current month is 5
high month
current month is 6
low month
current month is 7
high month
current month is 8
high month
current month is 9
low month
current month is 10
high month
current month is 11
low month
current month is 12
high month

Look at 2, it is shown as high month (hm), ie, it has 31 days.

I have commented out the line that would add 12 or December in the array :

#!/bin/bash
lm=()
lm+=(4)
lm+=(6)
lm+=(9)
lm+=(11)

hm=()
hm+=(1)
hm+=(3)
hm+=(5)
hm+=(7)
hm+=(8)
hm+=(10)
#hm+=(12)

feb=()
feb+=(2)

ld=(1 2)
hd=(1 2)
fd=(1 2)
for m in {1..12}
do
echo "current month is $m"
    if [[ " ${hm[*]} " == *"$m"* ]];
    then
        echo "high month"
    elif [[ " ${lm[*]} " == *"$m"* ]];
    then
        echo "low month"
    elif [[ " ${feb[*]} " = *"$m"* ]];
    then
        echo "feb"
    else
        echo "weird month input"
    fi
done

New output :

$ ./old2.sh
current month is 1
high month
current month is 2
feb
current month is 3
high month
current month is 4
low month
current month is 5
high month
current month is 6
low month
current month is 7
high month
current month is 8
high month
current month is 9
low month
current month is 10
high month
current month is 11
low month
current month is 12
weird month input

Now the output is as expected.

Why is my script accepting 2 as hm, instead of feb, as it should?

fraglord
  • 151
  • 1
  • 8
  • 1
    Did the answer work out for you? – Inian Sep 27 '18 at 08:52
  • 1
    In the future, try to follow [mcve] rules -- distilling your question to *the shortest possible code* (ideally just a few lines!) that shows the misbehavior. See also the "Tricks for Trimming" section at http://sscce.org/. – Charles Duffy Oct 01 '18 at 12:59
  • Note too that concatenating your array into a string and then doing a substring match on that resulting string is **really** not a reliable way to check membership. We already have Q&A entries that cover doing that right; ie. [Check if a bash array contains a value](https://stackoverflow.com/questions/3685970/check-if-a-bash-array-contains-a-value). – Charles Duffy Oct 01 '18 at 13:01
  • thank you Charles Duffy, I'll keep that in mind. Hello Inian, I cannot see your answer anymore. – fraglord Oct 04 '18 at 10:00

1 Answers1

0

It looks like the problem is that you are using *"$m"* as the matching pattern, as a result, when you try to match 2 y also match 12.

In your algorithm you used the whole array as a string and tried to match one element. One quick and dirty solution would be using spaces around your match like:

for m in {1..12}
do
echo "current month is $m"
    if [[ " ${hm[*]} " == *" $m "* ]];
    then
        echo "high month"
    elif [[ " ${lm[*]} " == *" $m "* ]];
    then
        echo "low month"
    elif [[ " ${feb[*]} " == *" $m "* ]];
    then
        echo "feb"
    else
        echo "weird month input"
    fi
done

There it will work, as " 2 " won't match " 12 ".

However I don't think that is the best approach... Unluckily, bash does not have a syntax to find an element inside an array, but if you check out this answer https://stackoverflow.com/a/8574392/6316852 there is a function that perfectly fits your needs.

So, another alternative:

#!/bin/bash
lm=(4 6 9 11)
hm=(1 3 5 7 8 10 12)
feb=(2)

containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

for m in {1..12}; do
    echo "current month is $m"
    if containsElement "$m" "${hm[@]}"; then
        echo "high month"
    elif containsElement "$m" "${lm[@]}"; then
        echo "low month"
    elif containsElement "$m" "${feb[@]}"; then
        echo "feb"
    else
        echo "weird month input"
    fi
done

hope it helps

Jorge Valentini
  • 307
  • 2
  • 14
  • If you believe a question to be duplicative (and I agree with you, it is!), note the bullet point regarding questions which "*...have already been asked and answered many times before*" in the "Answer Well-Asked Questions" section of [How to Answer](https://stackoverflow.com/help/how-to-answer). – Charles Duffy Oct 01 '18 at 13:02
  • BTW, even `containsElement` is slow (being O(n)) compared to an associative-array lookup; if the OP wants to be able to test whether a value exists in an array, they should store that in the *key* field, not as a value. (Bash "arrays" are quite misleadingly named -- they're really what other languages call hashes or maps). – Charles Duffy Oct 01 '18 at 13:03