# Can I use comparison operators in case?

I'm building a function that will calculate the gauge of wire required given amperage, distance(in feet), and allowable voltage drop.

I can calculate the "circular mils" given those values and with that get the AWG requirement. I started building a large `if` `elif` statement to compare the circular mils to it's respected gauge but I believe `case` is the right tool for this.

I haven't yet found any examples of case being used to compare numbers though so I'm wondering if it's even possible to do something like below:

``````what.gauge () {

let cmils=11*2*\$1*\$2/\$3
let amils=17*2*\$1*\$2/\$3

case \$cmils in

320-403)
cawg="25 AWG"
;;
404-509)
cawg="24 AWG"
;;
510-641)
cawg="23 AWG"
;;

etc...

}
``````
• "I believe `case` is the right tool for this" —honestly, the right tool for this is to use something other than Bash. Bash isn't really a programming language. It's extremely valuable for what it is, but numerical calculations should be done in other ways. And no, you can't use `case` for numerical comparisons in Bash. – Wildcard Aug 8 '17 at 2:05
• Aha. For pratical examples, I would do something with files and directories. That's what Bash excels at. Or perhaps chaining together multiple commands in a tricky pipeline. – Wildcard Aug 8 '17 at 2:12
• Ah, found what I was looking for: cmdchallenge.com Enjoy. :) – Wildcard Aug 8 '17 at 2:14
• (For the exact use case you describe, using `elif` chains is simplest, but it's not a great illustration of what Bash is really for. Nor is it something that I would want to see in production code.) – Wildcard Aug 8 '17 at 2:15
• There is a formula for calculating American Wire Gauge from circular mils; why not just apply that? – Blrfl Aug 8 '17 at 13:32

``````case \$cmils in

3[2-9][0-9]|40[0-3])
cawg="25 AWG"
;;
40[4-9]|4[1-9][0-9]|50[0-9])
cawg="24 AWG"
;;
51[0-9]|6[0-3][0-9]|64[01])
cawg="23 AWG"
;;
``````
• Thanks! This will do it, but I think writing up all that regex would take longer than the elifs...I guess I should be careful what I ask for lol. Thanks again though. – jesse_b Aug 8 '17 at 2:29
• This is technically the answer to the question that was asked. I would not want to be the person to maintain this code. – glenn jackman Aug 8 '17 at 2:36
• @glennjackman It is not even fun to write it. If for whatever reason something like that has to be used then you would write a function for generating the regexes and just use variables in the `case` selectors. – Hauke Laging Aug 8 '17 at 7:30
• International Obfuscated Bash Contest anyone? :) – pipe Aug 8 '17 at 12:31

### POSIX Shell

Since you are looking for arithmetic tests and case doesn't do arithmetic, `if-then` seems like the natural approach:

``````if [ "\$cmils" -lt 320 ]
then
cawg="??"
elif [ "\$cmils" -le 403 ]
then
cawg="25 AWG"
elif [ "\$cmils" -le 509 ]
then
cawg="24 AWG"
elif [ "\$cmils" -le 641 ]
then
cawg="23 AWG"
fi
``````

### Bash Only

I prefer portable solutions but some people are enamored of bash's arithmetic syntax:

``````if ((cmils < 320))
then
cawg="??"
elif ((cmils <= 403))
then
cawg="25 AWG"
elif ((cmils <= 509))
then
cawg="24 AWG"
elif ((cmils <= 641))
then
cawg="23 AWG"
fi
``````

This is both more powerful and more fragile than the POSIX syntax. To see why, try this code after setting `cmils=cmils`.

• Oh that's nice. I've seen that before but have never used parenthesis to compare like that. I'll have to read more into it. – jesse_b Aug 8 '17 at 2:31
• You would start reading here: gnu.org/software/bash/manual/… -- scroll down for `((...))` – glenn jackman Aug 8 '17 at 10:00

As others have said, `case` doesn't support comparison operators, just glob pattern matching.

However, you can make a a set of if/elif/fi statements look more like a case statement by formatting it differently. e.g., based on John1024's answer:

``````if   [ "\$cmils" -lt 320 ]; then cawg='??'
elif [ "\$cmils" -le 403 ]; then cawg='25 AWG'
elif [ "\$cmils" -le 509 ]; then cawg='24 AWG'
elif [ "\$cmils" -le 641 ]; then cawg='23 AWG'
fi
``````

or even:

``````[ "\$cmils" -ge 320 ] && [ "\$cmills" -le 403 ] && cawg='25 AWG'
[ "\$cmils" -ge 404 ] && [ "\$cmills" -le 509 ] && cawg='24 AWG'
[ "\$cmils" -ge 510 ] && [ "\$cmills" -le 641 ] && cawg='23 AWG'
``````

NOTE: unlike anything using `elif`, this variant has the disadvantage of running at least the first test of every one of those lines. Using `elif` will skip all remaining tests after any test evaluates to true. You could put something like this in a function and add `&& return` after setting `cawg`.

I personally find either of these to be far more readable (without all the extra linefeeds and alternating indentation cluttering things up), but opinions vary greatly on this particular coding-style/indentation issue :)

With everything lined up on the same (or very close) columns, they're easier to Yank, Paste, Edit too. Which is good when using the one true editor.

You can map a value into a range, and then use the index number of that range in a `case` statement:

``````cmil_limits=(320 404 510 642)
index=0
for limit in "\${cmil_limits[@]}"
do
if [ "\$cmils" -lt "\$limit" ]
then
break
fi
((index++))
done
case "\$index" in
0)                   # < 320
cawg="??"
;;
1)                   # 320-403
cawg="25 AWG"
;;
2)                   # 404-509
cawg="24 AWG"
;;
3)                   # 510-641
cawg="23 AWG"
;;
4)                   # > 641
cawg="??"
;;
esac
``````

If `\$cmils` is less than 320, we break out of the `for` loop on its first iteration, with `index=0`.  If `\$cmils` is ≮ 320 (i.e., is ≥ 320), we increment `index` (→`1`) and go on to the next iteration.  Then, if `\$cmils` is < 404 (i.e., ≤ 403, assuming that it is a whole number), we break out of the loop with `index=1`.  And so on.  If `\$cmils` is ≮ 642, it is ≥ 642 and hence > 641, so we run to the end of the `for` loop and get `index=4`.

This has the advantages that it keeps the cutoff values together, all on the same line, and you don’t have to maintain redundant numbers (e.g., your current code, and that of one other answer, lists both 403 and 404, and both 509 and 510 — which is redundant, and more work to maintain if the numbers ever change.  I don't know whether this is real-world concern.)

`cmil_limits` is an array.  bash, ksh, and some other shells support arrays, but some others do not.  If you need to do something like this in a shell that doesn’t support arrays, you can just put the list directly into the `for` statement:

``````for limit in 320 404 510 642
do
︙
``````

or use the shell’s parameter list as an array:

``````set -- 320 404 510 642
for limit in "\$@"
do
︙
``````

Some shells let you abbreviate the above:

``````set -- 320 404 510 642
for limit
do
︙
``````

`((…))` arithmetic is also a bashism (as is the `let` statement).  If you need to do something like this in a shell that doesn’t support `((…))` arithmetic, you can replace the

``````        ((index++))
``````

statement (to increment `index`) with

``````        index=\$(expr "\$index" + 1)
``````

Note that the spaces before and after the `+` are required.