Find first digit in string using expr index

I have looked for hours for an answer, so I have decided to request your guidance.

I want to substract the first number (series of digits) contained in a string. This string is the output of another command. The substring (number) can be located at any position inside the string.

I want to use only the bash string manipulation facilities, as to avoid using other commands (sed, awk, etc.).

The Advanced Bash-Scripting Guide: Manipulating Strings section specifies that index is used to get the "Numerical position in $string of first character in $substring that matches".

However, it does not state whether "substring" can be a regular expression or not. After trying things like

expr index "$string" '[0-9]'
expr index "$string" '[0-9]*'

I reached to the conclusion that substring cannot be a regular expression. Please confirm if this is correct. I know, I learn slow.

Now I found out that this can be solved with a command like

$(echo "$string" | /bin/awk '{print match($0,"[0-9]")}')

but this is what I wanted to avoid in the first place :slight_smile:

So I want to confirm with you guys if there is a way in which I could accomplish this without using external commands.

Thanks a lot, and I hope this post will eventually help others.

you can use expr match. Check the Manipulating Strings document again. read carefully

What I had in mind yesterday, but didn't have time to finish, was something along the following lines.

vnix$ s=dsfg123456sdf
vnix$ echo ${s#*[0-9]}
23456sdf
vnix$ echo ${s#${s#*[0-9]}?}
dsfg123456sdf
vnix$ echo ${s%?${s#*[0-9]}}
dsfg
vnix$ echo ${s#${s%?${s#*[0-9]}}}
123456sdf
vnix$ t=${s#${s%?${s#*[0-9]}}}
vnix$ echo ${t%[0-9]*}
12345
vnix$ echo ${t#${t%[0-9]*}?}
sdf
vnix$ echo ${t%${t#${t%[0-9]*}?}}
123456

It's probably too cumbersome to be of much practical utility, but it does work without any external command (as long as there is a single numeric substring).

Here's another rather monstrous idea (you can do it without Perl, although I would hate to have to):

vnix$ s=sdfd345sdfg436efg
vnix$ OLDIFS=$IFS
vnix$ IFS=`perl -e 'print map { chr($_) } (0..0x2F, 0x3A..0x7E)'`
vnix$ set -- $s
vnix$ IFS=$OLDIFS
vnix$ echo "$@"
    345    436  
> echo $s
dsfg123456sdf
> echo $s | tr [a-z] " " | tr -s " " | tr " " "\n" | head -2 | tail -1
123456

Let's go shorter

echo $s | tr -d a-z
.. or 
tr -d a-z <<< $s