5

How add I add the condition in case whereby if it does not detect the required conditions, it will execute the command.

My code:

case $price in
[0-9] | "." | "$") echo "Numbers, . , $ Only"
            ;;
esac

This command will execute if it detects numbers, "." and "$". How do change it in a sense if it does not detect those, the command will execute. Or are there other better commands to use to do this function.

6
  • If I recall, there is a way to use ANDs ORs and NOTs to create an XOR situation.
    – mdpc
    Commented Jan 13, 2015 at 5:31
  • Is it even possible to do the function I need ? @mdpc
    – Zac
    Commented Jan 13, 2015 at 5:35
  • Are you sure [0-9] | "." | "$" matches what you want? I would rather expect something like (in bash with extglob enabled): ?($)+([0-9])?(.+([0-9])) (matching $123.45 Commented Jan 13, 2015 at 5:47
  • Sorry, i just started shell about 2 weeks ago, do you mind explaining what your code does ? @HaukeLaging
    – Zac
    Commented Jan 13, 2015 at 5:50
  • That's the shell version of a regular expression. You should provide some example input for what and what shall not match. Commented Jan 13, 2015 at 6:00

3 Answers 3

7

Add a default case:

case $price in
[0-9] | "." | "$") true
            ;;
*) 
   do-something
   ;;
esac
3
  • can't get it to work, the true, is it a string ? Or it's a function
    – Zac
    Commented Jan 13, 2015 at 6:08
  • @Zac it's a command that does nothing and has exit status zero. Run /bin/true to check, if you wish. What's your error, exactly?
    – muru
    Commented Jan 13, 2015 at 6:10
  • still figuring out, it's a syntax error, but i tried your codes on a test script and it worked, thanks
    – Zac
    Commented Jan 13, 2015 at 6:18
2

This answer does not refer to the already explained case problem but to the matching problem.

First we need a definition what the valid strings may look like. The easiest definition would, of course, allow only one structure like

  1. start with one or more digits (leading zeroes allowed)
  2. decimal dot
  3. two trailing digits (if necessary 00)
  4. Dollar sign (no leading space)

As a regular expression (for e.g. grep, see man 7 regex) this would be written as:

^[0-9]+\.[0-9][0-9]\$$
  • ^ marks the beginning of the string i.e. there can be nothing before the [0-9]+
  • The dot is escaped \. in order to be treated as literal dot
  • The Dollar sign is escaped \$ in order to be treated as literal
  • the trailing $ marks the end of the string i.e. there can be nothing after the literal $

Testing:

> echo '0123.45$' | grep -E '^[0-9]+\.[0-9][0-9]\$$'
0123.45$

If the definition is changed so that both the dot with the trailing digits and the Dollar sign are optional then the regex would change to

^[0-9]+(\.[0-9][0-9])?\$?$

Testing:

> echo '0123$' | grep -E '^[0-9]+\.[0-9][0-9]\$$'

# no match
> echo '0123$' | grep -E '^[0-9]+(\.[0-9][0-9])?\$?$'
0123$
> echo '0123.45$' | grep -E '^[0-9]+(\.[0-9][0-9])?\$?$'
0123.45$

The shell can work with regular expressions directly but not within case patterns. You need the [[ ]] structure. But as you want to know only whether a string matches or not anyway there is no reason to use case:

if [[ "$price" =~ ^[0-9]+(\.[0-9][0-9])?\$?$ ]]; then
  :
else
  :
fi

If you really need case then you can set the option extglob with the shell bulitin shopt and rewrite the regex to a "shell regex":

shopt -s extglob

^[0-9]+(\.[0-9][0-9])?\$?$ becomes then

+([0-9])?(.[0-9][0-9])?($)

leading $

If you want $0123.45 instead of 0123.45$ then you obviusly have to put the check for $ at the start:

  • grep / [[ ]]: ^\$?[0-9]+(\.[0-9][0-9])?$
  • shell pattern matching: ?($)+([0-9])?(.[0-9][0-9])

easier check

If you don't care about the order but just about the right chars then you can use much easier expressions:

  • grep / [[ ]]: ^[0-9.$]+$
  • shell pattern matching: +([0-9.$])

example

#! /bin/bash

shopt -s extglob

for value in 1234 1234EUR; do
    case "$value" in
            +([0-9]))
                    echo "value OK: '${value}'"
            ;;
            *)
                    echo "value not OK: '${value}'"
            ;;
    esac
done
16
  • sorry for the dup, after many tries, i still can't get it to work, what do i have to put into the case condition without adding other stuff. "$" | [0-9]* | "." currently using this to catch for un-required characters
    – Zac
    Commented Jan 18, 2015 at 18:45
  • guess i'm gonna use if, tried your code, if i key in $10.00, it failed, any help on this ?
    – Zac
    Commented Jan 18, 2015 at 18:53
  • @Zac I have extended the answer by a trivial change. If an answer doesn't work for you then write a comment to that answer. Do not ask a new question about that; it would get closed. Commented Jan 18, 2015 at 19:17
  • thanks, works perfect, learn something new as well, sorry, i'm still new to this.
    – Zac
    Commented Jan 18, 2015 at 19:26
  • sorry to bother you again, i have another function with requires a simple check, and i was hoping not to use if, just by using case, the condition is simple, just to check if the number is a positive whole number, i know [0-9]* checks for positive number, but it doesn't check for whole number, what should i do ?
    – Zac
    Commented Jan 18, 2015 at 19:36
0

To match a string containing only a single, positive integer with a shell case statement you can do:

case "${string:--}" in
(*[!0-9]*)  
! echo 'Invalid value for $string!';;
(*[!0]*) 
echo "$string is a positive integer greater than 0.";;
(*)
echo "$string is 0.";;
esac

In the first case match I screen for any value of the ${string:--} expansion that contains even a single character which is not among 0123456789. In the event $string is either unset or null it will expand to - and so still match this pattern. The leading ! inverts echo's return (which is always 0) to a 1 - and so the case construct returns 1 if $string contains an invalid value.

In the next I match any value for $string containing a single digit which is not 0 - and so any number of leading 0s on any valid value for $string will match as a positive number.

The last matches any which did not match the previous two - which is to say it matches only one or more 0s.

You must log in to answer this question.

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