12

I have a string with components and version numbers:

data-c(kuh-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(kuh)-win2

For a shell script, I need to extract the version number of the divider binary. So I need to yield:

1.4.4

What would be a good way to do this? with sed?

mles
  • 4,534
  • 10
  • 54
  • 94
  • 1
    @fedorqui You are right! I think I wanted to point out http://stackoverflow.com/questions/6655276/sed-extract-version-number-from-string I delete my first comment, and replace it by another. – oHo Jun 07 '16 at 19:11
  • See also a [similar answer](http://stackoverflow.com/questions/6655276/sed-extract-version-number-from-string) but about `sed` (there are some few answers based on commands `cut`, `perl` and `awk`). – oHo Jun 07 '16 at 19:13

5 Answers5

18

Following Kent's answers, this can work:

grep -Po '(?<=divider-bin-)\d.\d.\d'

and even better:

grep -Po '(?<=divider-bin-)[^;]+'

it greps from divider-bin- until it find the ; character. This way any NNN.NNN. ... . NNN format will work (no matter how many blocks of NN).

Test:

$ echo "data-c(kuh-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(kuh)-win2" | grep -Po '(?<=divider-bin-)[^;]+'
1.4.4
$ echo "data-c(kuh-small1);divider-bin-1.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(kuh)-win2" | grep -Po '(?<=divider-bin-)[^;]+'
1.4
Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • how to grep it until it finds the second `;` character? – Koustuv Sinha Jun 03 '16 at 07:21
  • @KoustuvSinha I would need some sample input to know what you are referring to. – fedorqui Jun 03 '16 at 07:59
  • like I have a similar requirement, where I put the stop character as `\s` to get a version number, say 3.2.1 Now, i want to get only upto 3.2. Using stop character as `.` i can get only upto 3 – Koustuv Sinha Jun 03 '16 at 10:05
  • @KoustuvSinha `grep -Po '(?<=divider-bin-)\d.\d'` should make it. Otherwise, ask a new question providing more details. – fedorqui Jun 03 '16 at 10:11
  • just living a comment because it happened to me but if -P is not available in your grep version you can go with the sed, aswer to my question: "Then use sed, sed -n 's/.*(version) = "\([0-9.]*\).*/\1/p'. See demo. – Wiktor Stribiżew Oct 1 at 9:23" https://ideone.com/Niylke – Miguel Costa Oct 06 '20 at 06:50
  • It doesn't work. It gives: ```invalid option -- P``` – MA1 May 31 '22 at 19:42
  • @MA1 are you using GNU Grep? – fedorqui Jun 01 '22 at 10:08
  • @fedorqui I'm using (BSD grep, GNU compatible). Is there any other ways to accomplish this without installing extra tools? – MA1 Jun 01 '22 at 14:13
  • @MA1 you need GNU grep to have this answer work. Otherwise, you can use Perl or sed, as recommended by other answers here. – fedorqui Jun 01 '22 at 14:26
7

This general answer should work in all cases

I use one of these three one-line commands depending on the expected input string:

  1. The input string always contain one single version

     perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' 
    
  2. To extract several versions (several lines)

     perl -pe 'if(($_)=/([0-9]+([.][0-9]+)+)/){$_.="\n"}'
    
  3. To extract one single version (the first one) in all cases

     perl -pe 'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v\n";exit}$_=""'
    

  1. The simplest

The first command line do not embed the final newline:

> gcc --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
5.3.1

> bash --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
4.2.46

> perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' <<< 'A2 33. Z-0.1.2.3.4..5'
0.1.2.3.4

> uname -a | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
3.10.0

> lsb_release | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
4.1

Store the version within a shell variable:

> v=$( gcc --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
> echo "GCC version is '$v'"
GCC version is '5.3.1'

But fails for multi version numbers:

> gwenview --version
Qt: 4.8.5
KDE Development Platform: 4.14.8
Gwenview: 4.10.4

> gwenview --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
4.8.54.14.84.10.4
  1. Extract one version per line

> gwenview --version | perl -pe 'if(($_)=/([0-9]+([.][0-9]+)+)/){$_.="\n"}' 
4.8.4
4.14.8
4.10.4

> mvn --version
Apache Maven 3.0.4
Maven home: /usr/share/maven
Java version: 1.7.0_25, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
Default locale: fr_FR, platform encoding: UTF-8
OS name: "linux", version: "3.11.0-13-generic", arch: "amd64", family: "unix"

> mvn --version | perl -pe 'if(($_)=/([0-9]+([.][0-9]+)+)/){$_.="\n"}'
3.0.4
1.7.0
3.11.0
  1. Extract the first version only

> gwenview --version | perl -pe 'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v\n";exit}$_=""'
4.8.5

Create an alias:

> alias extractor='perl -pe '\''if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v\n";exit}$_=""'\'''

or if you use a recent :

> alias extractor=$'perl -pe \'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v\n";exit}$_=""\''

> v=$( mvn --version | extractor )
> echo "The maven version is '$v'"
The maven version is '3.0.4'
reixa
  • 6,903
  • 6
  • 49
  • 68
oHo
  • 51,447
  • 27
  • 165
  • 200
2

sed can handle this easily....

string="ata-c(kuh-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(kuh)-win2"

echo $string | sed "s/^.*divider-bin-\([0-9.]*\).*/\1/"
1.4.4

There are a few other things you can do to tighten it up... such as stop grabbing the version number when you reach the ";"

sed "s/^.*divider-bin-\([^;]*\).*/\1/"
0

You can also use perl:

perl -wne '/divider-bin-(.+?);/ and print "$1\n"
michas
  • 25,361
  • 15
  • 76
  • 121
0
sed -n "s/.*;divider-bin-\([0-9][^;]*\);.*/\1/p"

infos is between ;divider-bin- and next ; and start with a digit. This is to ensur that no Other-divider-bin- or divider-bin-and-hex- will interfere with your request. Also, return empty string if not find. to be exhaustif (assuming version is only digit and dot)

sed -n "s/.*;divider-bin-\([0-9.]\{1,\}\)\([^0-9.;][^;]*\)*;.*/\1/p"
NeronLeVelu
  • 9,908
  • 1
  • 23
  • 43