5

I have if loop in my shell script which will check the is the table name is having any integer present this is the condition I am specifying:

if [[ "$able_name" == *[0-9]* ]] 

Generally, the table name will be like this:

tablename_000 

or

tablename_001 

But I have a new file whose name is something like table_V2. Since the name matches the regex, it's entering this loop. How can I avoid this? It should enter the if loop only if integers are present after the underscore and not any other character.

  • Note that you are not actually using regular expressions here. That's a glob. See this question for more details on the difference between the two. – terdon Oct 12 '17 at 14:49
5

With standard sh syntax:

case ${table_name##*_} in
  ("$table_name" | "" | *[!0-9]*) echo >&2 incorrect;;
  (*) echo correct;;
esac

That is check that $table_name stripped of everything up to the right-most _ in it is neither $table_name itself (which would mean $table_name had no _), nor the empty string, nor contained a non-digit character.

The standard command to match a string against a regexp is expr though it has a few issues and doesn't make for very legible code:

if expr " $table_name" : '.*_[0-9]\{1,\}$' > /dev/null; then
  echo Correct
else
  echo >&2 Incorrect
fi

The leading space is to avoid issues for values of $table_name like + or --help. regexps are anchored at the beginning implicitly (hence the .*) but not end (hence the $) and the result (0 or 1 here) is output on stdout in addition to being reflected in the exit status, hence the redirection to /dev/null.

Some [ implementations like the [ builtin of zsh and yash have a =~ operator for that (using ERE, though you can change that to PCRE with zsh):

if [ "$table_name" '=~' '_[0-9]+$' ]; then
   echo Correct
else
   echo >&2 Incorrect
fi

bash, zsh and ksh93 have a =~ operator inside their [[...]] construct, though the syntax and the behaviour wrt quoting varies between implementations. Best is to use variables as already shown by @BLayer

zsh -o extendedglob and ksh (or bash -O extglob or zsh -o kshglob that support a subset of ksh globs) have glob operators that are functionally equivalent to regexps albeit with a different syntax.

Translation RE -> ksh-glob / zsh-glob:

  • [0-9] -> [0-9] / [0-9]
  • x+ -> +(x) / x##
  • $ or ^ -> implicit / implicit
  • . -> ?
  • .* -> * (or *(?)) / * (or ?#)

So in ksh (or bash -O extglob or zsh -o kshglob):

case $table_name in
  (*_+([0-9]) echo correct;;
  (*) echo >&2 incorrect;;
esac

In zsh -o extendedglob:

case $table_name in
  (*_[0-9]##) echo correct;;
  (*) echo >&2 incorrect;;
esac

zsh also has the <x-y> extended glob operator to match decimal numbers from x to y so you can also write it (*_<->) echo correct.

In those shells, those globs can also be used on the right hand side of the = aka == [[...]] operator.

3

Probably the most simple regex:

if [[ "$file" =~ _[0-9]+$ ]]
then
    echo OK $file
fi
3

Many ways to do this. Here's a Bash version that goes the traditional regex way (or at least Bash's closest approximation to it):

pattern='^tablename_[[:digit:]]+$'
if [[ $filename =~ $pattern ]]; then
    echo "Filename $filename is valid"
fi

A couple notes:

  • For all but the simplest regular expressions it is advisable to use a variable as I've done here with pattern. Even though no word splitting or pathname expansion is applied to the expression inside [[ ]] there will be tilde, variable, and arithmetic expansion as well as process and command substitution. It's too easy to get incorrect or unexpected results with inline regex.
  • I'm using POSIX character class [:digit:] but 0-9 is fine, too. I figured most of the other answers here would use the latter and it would be worthwhile to show the former for the sake of completeness

Commentary: The fact that there are so many variations on this theme (in this thread alone) is one reason why some people don't like sh/bash...and why I love them. :)

2

Recommend using a regex option for this, but a normal glob for digits only could also be used. For example you could do something like

for file in tablename_[0-9]*; do
    [ -f "$file" ] || continue
    printf "%s\n" "$file"
done

Using the regex operator in bash, you could do something like,

for file in tablename_*; do
    if [[ $file =~ _([[:digit:]]+)$ ]]; then
        printf "%s\n" "$file"
    fi
done
  • 1
    tablename_[0-9]* matches on tablename_1V and tablename_1_V1 – Stéphane Chazelas Oct 12 '17 at 14:53
  • @StéphaneChazelas: Absolutely! I did the glob and regex pattern solely on the OP's original examples. Assuming they don't have the names as you said – Inian Oct 12 '17 at 14:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.