0

Given a shell variable whose value is a semantic version, how can I create another shell variable whose value is (tuple 1 × 1000000) + (tuple 2 × 1000) + (tuple 3) ?

E.g.

$ FOO=1.2.3
$ BAR=#shell magic that, given ${FOO} returns `1002003`
# Shell-native string-manipulation? sed? ...?

I'm unclear about how POSIX-compliance vs. shell-specific syntax comes into play here, but I think a solution not bash-specific is preferred.


Update: To clarify: this isn't as straightforward as replacing "." with zero(es), which was my initial thought.

E.g. The desired output for 1.12.30 is 1012030, not 100120030, which is what a .-replacement approach might provide.


Bonus if the answer can be a one-liner variable-assignment.

phuclv
  • 37,963
  • 15
  • 156
  • 475
StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • which shell is that? bash, zsh, ksh... are all different and can result in significantly different code – phuclv Apr 30 '21 at 00:17
  • @phuclv - I believe that I cannot assume which shell. Most likely bash...but can't say for sure. – StoneThrow Apr 30 '21 at 00:18
  • @StoneThrow : You basically have 2 separate problems: (1) Cutting `FOO` into its constituents (1, 2, 3) , and the multiplying the constituents (i.e. you need integer arithmetic). Right? So these are two sub-problems, and if you are stuck with one of them, ask a question only for this particular problem, and also show your own attempt. – user1934428 Apr 30 '21 at 09:12
  • @stoneThrow : Most likely bash...but can't say for sure .... Sorry, but this is insane. You want to write a program, but don't are not sure which programming language you want to use? Make up your mind first about this. Different languages will possibly lead to different solutions. – user1934428 Apr 30 '21 at 09:14

4 Answers4

2

A perl one-liner:

echo $FOO | perl -pne 's/\.(\d+)/sprintf "%03d", $1/eg'

How it works:

  • perl -pne does a REPL with the supplied program
  • The program contains a replacement function s///
    • The search string is the regex \.(\d+) which matches a string beginning with dot and ends with digits and capture those digits
    • The e modifier of the s/// function evaluates the right-hand side of the s/// replacement as an expression. Since we captured the digits, they'll be converted into int and formatted into leading zeros with sprintf
    • The g modifier replaces all instances of the regex in the input string

Demo

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Nice. Can you offer an explanation on how that works? By experiment, it delivers the required result. And I assume the use of Perl mitigates problems about POSIX-compliance. But I can't readily decipher how that one-liner is doing things like "multiply by 1000000" or "multiply by 1000" as is needed to get the required answer format. – StoneThrow Apr 30 '21 at 00:17
  • @StoneThrow added some explanation – phuclv Apr 30 '21 at 00:25
  • Am I correct to say that by using Perl, we avoid any concern about POSIX-compliance of the variable-assignment syntax (like my answer below that uses bash arrays)? – StoneThrow Apr 30 '21 at 00:28
  • @StoneThrow sorry I'm not a language lawyer and I don't know where to read in that huge standard. In practice it seems all systems I've had access does have perl by default – phuclv Apr 30 '21 at 00:41
1

Split on dots, then loop and multiply/add:

version="1.12.30"

# Split on dots instead of spaces from now on
IFS="."

# Loop over each number and accumulate
int=0
for n in $version
do
  int=$((int*1000 + n))
done

echo "$version is $int"

Be aware that this treats 1.2 and 0.1.2 the same. If you want to always treat the first number as major/million, consider padding/truncating beforehand.

that other guy
  • 116,971
  • 11
  • 170
  • 194
0

This should do it

echo $foo | sed 's/\./00/g'
phuclv
  • 37,963
  • 15
  • 156
  • 475
Ruchi
  • 679
  • 3
  • 13
  • 1
    Close! I was thinking of something similar, but not quite right. This is substituting `.` with `00` which is slightly different than the required output. E.g. given `1.12.30` this returns `100120030`, which is different than the required output of `1012030`. – StoneThrow Apr 29 '21 at 23:41
  • Here's a revision that stops short of calculating: `echo $foo| awk -F. '{for(i=1; i<=NF; i++) {print $i*1000^(NF-i)"+"}}'` – Ruchi Apr 30 '21 at 00:45
0

How about this?

$ ver=1.12.30
$ foo=$(bar=($(echo $ver|sed  's/\./ /g')); expr ${bar[0]} \* 1000000 + ${bar[1]} \* 1000 + ${bar[2]})
$ echo $foo
1012030
StoneThrow
  • 5,314
  • 4
  • 44
  • 86