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.

terdon
  • 242,166
Rahul
  • 503
  • 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

4 Answers4

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

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. :)

B Layer
  • 5,171
3

Probably the most simple regex:

if [[ "$file" =~ _[0-9]+$ ]]
then
    echo OK $file
fi
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
Inian
  • 12,807