Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I would like to extract the 2nd field from the last part of the following strings:

foo_1.103.debug_xx.ver21-inc-1          --> string extracted "debug_xx"
foo_1.103.1.0.release_32_xx.ver21-inc-1 --> string extracted "release_32_xx"
foo_1.103.1.0.release_xx.ver21-inc-1    --> string extracted "release_xx"

Any help will be appreciated.

share|improve this question
up vote 1 down vote accepted

Use ${VAR%PATTERN} to remove the suffix corresponding to the last field, then ${NEWVAR##PATTERN} to remove the prefix corresponding to all but the last remaining field.


You need to store the string in a variable and store the intermediate result in a variable as well, you can't directly chain the expansions (you can in zsh, but not in bash).

share|improve this answer
you have a minor typo, your second statement should have referred to $all_but_last_field like second_to_last_field=${all_but_last_field##*.}. I'm accepting your answer as it's pure bash but the answer of Drake Clarris was great as well. It's actually even more compact but I can only choose 1 answer. Thanks guys :) – icasimpan Dec 28 '12 at 0:56

With pure bash:

IFS='.' read -a p <<< 'foo_1.103.1.0.release_32_xx.ver21-inc-1'
echo "${p[${#p[@]}-2]}"

You can do it easier with awk:

awk -F. '{print$(NF-1)}' <<< 'foo_1.103.1.0.release_32_xx.ver21-inc-1'

In case your strings to parse are file names, you can still do it with awk:

awk -F. 'BEGIN{for(i=1;i<ARGC;i++){$0=ARGV[i];print$(NF-1)}}' foo*
share|improve this answer
+1 for the best possible answer. Your echo would be nicer thus: echo "${p[@]:(-2):1}". – gniourf_gniourf Dec 27 '12 at 10:30
Cleaner, shorter, faster. Great one, @gniourf_gniourf. – manatwork Dec 27 '12 at 11:02
gniourf_gniourf & manatwork: great solution. I'm not so well versed with awk but with your help, I'm learning some more about it. +1 to both of you :) – icasimpan Dec 28 '12 at 0:53

A sed command to do it:

sed 's/foo_[0-9.]*\([^.]*\).*/\1/' inputfile

Assuming, of course, that inputfile contains your strings, each on its own line (as your example appears to be). If say you are using file names, then piping the results of ls into the sed will work as well.

share|improve this answer
Good one, but that [0-9.] is its weakness – will fail on a file name like “foo_1.103b.debug_xx.ver21-inc-1”. Better rely strictly on dots: sed 's/.*\.\([^.]*\)\.[^.]*$/\1/'. – manatwork Dec 27 '12 at 15:15
Yeah I used the numbers because in his example that seemed more consistent than the dots. – Drake Clarris Dec 27 '12 at 17:55
+1 to Drake Clarris for providing original solution and +1 for you manatwork too for reviewing the code. But yup, I agree with the Drake Clarris' solution, he was trying to be much more practical. But your solution would help in case my pattern changes :) – icasimpan Dec 28 '12 at 0:37

assuming those are files:

ls foo*|awk -F. '{print $3}'

share|improve this answer
-1 It is not the third column based on dots for all entries – Bernhard Dec 27 '12 at 9:56
oh really? As far as I know the index start at 1 not 0 ... And just tested it under linux bash 4.1.2 ... foo_1.103.debug_xx.ver21-inc-1 $1= foo_1 $2=103 $3=debug_xx $4=ver21-inc-1 test it for yourself ... – BitsOfNix Dec 27 '12 at 9:59
Test it again: pastebin.com/wvsPm7AV – manatwork Dec 27 '12 at 10:01
pastebin.com/M0rnVMUT – BitsOfNix Dec 27 '12 at 10:06
@AlexandreAlves, gniourf_gniourf is referring to GreyCat's Why you shouldn't parse the output of ls(1) article. In case the file names would contain space, like “foo_1.103.de bug_xx.ver21-inc-1”, your ls based solution would display “de\ bug_xx”. This can be corrected with ls's --quoting-style=literal. But if the file names would contain new line, the parsing would became more complicated. – manatwork Dec 27 '12 at 10:56

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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