0

can I use a function instead of regular patterns, strings etc. in a bash case statement like this?

#!/bin/bash

var="1"

is_one () {
[$var -eq 1]
}

case $var in

    is_one) 
    "var is one"
    ;;
    
    *) 
    "var is not one"
    break
    ;;
    
esac
von spotz
  • 875
  • 7
  • 17
  • 1
    Btw: You don't need `break` inside bash's `case` statement. Bash uses `;;` instead. Also, you need spaces around `[` and `]`. – Socowi May 27 '21 at 07:11

3 Answers3

5

You can use functions in statements where could also use programs like test/[, that is while, until, and if. The case statement however allows only patterns, no programs/functions.

Either use

if is_one; then
  echo "var is one"
else
  echo "var is not one"
done

or

case $var in
  1) echo "var is one" ;;
  *) echo "var is not one" ;;
esac
Socowi
  • 25,550
  • 3
  • 32
  • 54
4

Of course you can use a function, but the function should print the pattern you want to match as a glob. That's what case operates on.

is_one () {
    echo 1
}

case $var in
   "$(is_one)") echo 'It matched!';;
   *) echo ':-(';;
esac

Perhaps a better design if you really want to use a function is to write a one which (doesn't contain syntax errors and) returns a non-zero exit code on failure. In this particular case, encapsulating it in a function doesn't really make much sense, but of course you can:

is_one () {
    [ "$1" -eq 1 ]
}
if is_one "$var"; then
    echo 'It matched!"
else
    echo ':-('
fi

Notice the non-optional spaces inside [...] and also the quoting, and the use of a parameter instead of hard-coding a global variable.

Furthermore, note that -eq performs numeric comparisons, and will print an unnerving error message if $var is not numeric. Perhaps you would prefer to use [ "$1" = "1" ] which performs a simple string comparison. This is also somewhat closer in semantics to what case does.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • So it doesn't work to evaluate whether the method "matches", e.g. is true or falsey? In the end, for truth-tests I need a dangling `if-else statement` ? – von spotz May 27 '21 at 07:23
  • 1
    You can of course write a function which returns true or false, but `case` works exclusively on patterns, so then you'd have to use a different flow control statement, yes. I don't see how you would need specifically a "dangling `if-else` statement" if I understand correctly what you mean by that. With the definition in your question (except of course with the syntax errors fixed, as pointed out elsewhere) you could do `if ! is_one; then echo "is not"; fi` if you only want to cover the failure. – tripleee May 27 '21 at 07:54
  • 1
    @vonspotz : For _trueish_ and _falsey_, you need `if`, `until` or `while`. The reason is that they _run a command_ and react on its exit code, while `case` does not run a command. – user1934428 May 27 '21 at 08:52
0

The main difference is that if... elif... distinguishes its alternatives based on the exit codes of the commands being executed, while case.... bases it on string values. Your function, when invoked, exhibits its result only via its exit code.

If you want to use your function with a case, you need to turn this exit code into a string, for instance like this:

is_one
var=$? # Store exit code as a string into var
case $var in
1) echo "exit code is one" ;;
101) echo "exit code is onehundredandone" ;;
*) echo "I do not care what the exit code is" ;;
esac
Socowi
  • 25,550
  • 3
  • 32
  • 54
user1934428
  • 19,864
  • 7
  • 42
  • 87
  • What's with `echo "true"` or `echo "false"` ? Wouldn't that also be possible without quotes? – von spotz May 27 '21 at 07:45
  • Quoting is optional here, but good practice especially for beginners. In short, quote everything until you find a situation where quoting actually breaks something. For detailed advice, see [When to wrap quotes around a shell variable](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee May 27 '21 at 07:52
  • But see also [Why is testing “$?” to see if a command succeeded or not, an anti-pattern?](https://stackoverflow.com/questions/36313216/why-is-testing-to-see-if-a-command-succeeded-or-not-an-anti-pattern) – tripleee May 27 '21 at 07:55
  • @triplee: It often is, but not always. Imagine the case that you run some functions, need to collect their exit codes and later on make a decision. There is no alternative to storing `$?` somewhere. Of course in this concrete toy example, nobody would really want to use `case`, and with using `if`, we avoid the problem alltogether. But the OP wants (perhaps as a programming exercise) see whether `case` could be used here too, and then we need `$?`. Actually, the reason why I assigned it to `var` instead of using it directly inside `case` statement, is to avoid the common pitfalls with `$?`. – user1934428 May 27 '21 at 08:46
  • @vonspotz: You have to be careful for instance when the string contains several spaces in a row. For instance, `echo "I am hungry"` and `echo I am hungry` produce different output. – user1934428 May 27 '21 at 08:49