LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   extracting floating numbers from variable using bash's builtin string chopping (https://www.linuxquestions.org/questions/programming-9/extracting-floating-numbers-from-variable-using-bashs-builtin-string-chopping-740587/)

billywayne 07-16-2009 01:36 PM

extracting floating numbers from variable using bash's builtin string chopping
 
Hi. I'm wanting to use bash3 to chop up some strings! :-)

Here's the string I want to chop:

Code:

C9-1.4-1.6
, which I'm assigning to a variable that I'm naming $window.

I'm wanting to chop out the 1.4 and the 1.6. The 1.6 is simple enough with something like

Code:

echo "${window##*-}"
which, of course, chops off the beginning of the string until the final -.

What I'm having trouble with is getting the 1.4 out of there. I can do it with a simple

Code:

echo "${window:3:3}"
to pull out the substring, but the thing is, sometimes the window I'm manipulating is something like

Code:

C10-1.4-1.6
, in which case the above will give me

Code:

-1.
which obviously isn't what I'm looking for.

So, to summarize, I have a string that contains two 2-digit floating point numbers, separated off by dashes(-), and I want to extract these and only these strings.

Any help appreciated!


billywayne

nc3b 07-16-2009 01:57 PM

Hello. Here is one solution (surely not the only one and surely not the best):

Code:

var='C9-1.4-1.6'
var="${var#*-}"
echo "${var%%-*}"

This will give you 1.4, but it will do so in two steps. Waiting to see if there is a nicer way. Hope this helps though.


Cheers

David the H. 07-16-2009 02:00 PM

echo "${window#*-}"

If you use only one hashmark, it stops at the first match.

Edit: After seeing the above post, I think I misunderstood the question. You want to extract only the middle number? I don't think that's generally doable in one step through parameter substitution (except by the 3:3 column matching you already know about). You have to use multiple steps as above, or an external tool like grep, sed, or awk for that.

billywayne 07-16-2009 03:02 PM

OK. Thanks for the replies.

I was hoping for something like a

Code:

echo ${var#*-%-*}
but d'oh well. thanks, again.


billywayne

ntubski 07-16-2009 03:26 PM

Here's a way that technically extracts the everything in one step, although you can't echo it in the same command.

Code:

window='C9-1.4-1.6'
IFS=- read one two three <<<"$window"
echo "one = $one, two = $two, three = $three"

EDIT:
thought of something like ${var#*-%-*}, kind of ugly though
Code:

shopt -s extglob # requires extended globs
echo ${var//@(+([^-])-|-+([^-]))/}


ghostdog74 07-16-2009 08:21 PM

Code:

# IFS="-"
# a="C9-1.4-1.6"
set -- $a
# echo $1
C9


David the H. 07-17-2009 03:42 AM

Quote:

Originally Posted by ntubski (Post 3609924)
thought of something like ${var#*-%-*}, kind of ugly though
Code:

shopt -s extglob # requires extended globs
echo ${var//@(+([^-])-|-+([^-]))/}


Actually, that's rather neat. I should've known that there would be a shell option for things like this (note to self: read up on shopt). Don't forget to turn it off again when you're finished with it (shopt -u extglob).

Quote:

Originally Posted by ghostdog74
Code:

# IFS="-"
# a="C9-1.4-1.6"
set -- $a
# echo $1
C9


This is also pretty cool, but you should be aware that it also unsets all of the previous positional parameters, as well as replacing the first three. This could have rather undesirable effects inside a script if you're not careful.

In the end though, it's seems obvious that there's no way to do it in a single line. It's going to take two or more commands to extract the middle portion no matter what you try. I think ntubski's solution above is the cleanest one, personally, if you really need it all done in one operation. Otherwise, the simplest solution is probably just to step it through two iterations of variable expansion.
Code:

var="C9-1.4-1.6"
var="${var#*-}"
echo "${var%-*}"


ghostdog74 07-17-2009 04:09 AM

Quote:

Originally Posted by David the H. (Post 3610445)
This is also pretty cool, but you should be aware that it also unsets all of the previous positional parameters, as well as replacing the first three. This could have rather undesirable effects inside a script if you're not careful.

just unset it back after every usage
Code:

unset IFS

David the H. 07-17-2009 04:27 AM

Quote:

Originally Posted by ghostdog74 (Post 3610474)
just unset it back after every usage
Code:

unset IFS

You missed my point. It's not IFS that concerns me, but the $1, $2, etc. positional parameters. If any subsequent code in your script relies on the original input from higher-numbered positionals (there's a line that uses $4, for example), then using your line above will break it, because "set --" will unset them. And of course the original values of $1, $2, and $3 are all overwritten.

It's not necessarily a fatal flaw, but it could cause problems if you're not careful.

ghostdog74 07-17-2009 05:03 AM

Quote:

Originally Posted by David the H. (Post 3610486)
You missed my point.

an example might be more clear in what you are trying to explain. i am an english idiot. :)

vonbiber 07-17-2009 06:32 AM

Quote:

Originally Posted by billywayne (Post 3609824)
Here's the string I want to chop:

Code:

C9-1.4-1.6
, which I'm assigning to a variable that I'm naming $window.

I'm wanting to chop out the 1.4 and the 1.6. simple

I have a string that contains two 2-digit floating point numbers, separated off by dashes(-), and I want to extract these and only these strings.

why don't you just use the 'cut' command?

str1=$(echo $window | cut -d'-' -f1)
str2=$(echo $window | cut -d'-' -f2)
str3=$(echo $window | cut -d'-' -f3)

ghostdog74 07-17-2009 06:52 AM

Quote:

Originally Posted by vonbiber (Post 3610578)
why don't you just use the 'cut' command?

because its not necessary to call it 3 times on 1 variable.

catkin 07-17-2009 08:35 AM

Hello :)

Just to add another parsing technique (ntubski's ${var//@(+([^-])-|-+([^-]))/} is already a magnificent solution), how about
Code:

$ a="C9-1.4-1.6"
$ IFS='-'
$ array=($a)
$ unset IFS
$ echo ${array[1]} ${array[2]}
1.4 1.6

Best

Charles

ntubski 07-17-2009 11:43 AM

Quote:

Originally Posted by David the H. (Post 3610445)
Don't forget to turn it off again when you're finished with it (shopt -u extglob).

Or you could just have it on throughout the script.

Quote:

Originally Posted by ghostdog
just unset it back after every usage
Code:

unset IFS

As far as I know, unset'ing IFS won't restore the previous value.

catkin 07-17-2009 12:14 PM

Thanks ntubski :)
Quote:

Originally Posted by ntubski (Post 3610886)
Or you could just have it [shopt -u extglob] on throughout the script

I set it in the shell initialisation; why not?
Quote:

Originally Posted by ntubski (Post 3610886)
As far as I know, unset'ing IFS won't restore the previous value.

I doesn't; that will teach me not to unquestioningly accept what is posted here! My own scripting idiom is
Code:

oIFS="$IFS"
IFS="<whatever>"
<commands>
IFS="$oIFS"

Best

Charles


All times are GMT -5. The time now is 04:36 AM.