I did a few research, I can split the string by '.' but the only thing I want to replace is the third '.', and the version number is actually variable so how can I do the replace/split?


but What I want is the output like this:

version: 1.8.0-110

Used sed for example:

$ echo 'version:' | sed 's/\./-/3'
version: 1.8.0-110


sed s/search/replace/x searches for a string and replaces the second. x determines which occurence to replace - here the 3rd. Often g is used for x to mean all occurances.

Here we wish to replace the dot . but this is a special character in the regular expression sed expects in the search term. Therefore we backslashify the . to \. to specify a literal ..

Since we use special characters in the argument to sed (here, the backslash \) we need to put the whole argument in single quotes ''. Many people always use quotes here so as not to run into problems when using characters that might be special to the shell (like space ).

| improve this answer | |

Use bash

You do not need an external program to do this, especially since it appears you want to replace the last period with a dash. Bash can handle string manipulations itself.

Presuming you have


the answer is simply:

$ echo ${vers%.*}-${vers##*.}

The % means delete the shortest string to the right that matches. The ## means delete the longest string to the left that matches.

What if there are more than three periods?

If you actually want to replace the third period, not the last period as I presumed above, it gets a little trickier in bash, but it is still possible. For example, if vers='version:!', you can find everything after the third period and use that to truncate the string:

$ end=${vers#*.*.*.}
$ start=${vers%$end}
$ echo ${start%.}-$end
version: 1.8.0-110.hut.hut.hut.hike!

Another way: use IFS to split string into a bash array

While this is overkill for the question, it is sometimes useful to split a string into components and load them into an array.

IFS=. read -a foo <<<'version:!'

You can iterate over the array $foo and choose what to print when. For example,

echo -n $foo
for ((i=1; i<${foo[@]}; i++)); do 
  [[ i -eq 3 ]] && echo -n - || echo -n .
  echo -n ${foo[i]};
| improve this answer | |

In this case, the third is also the last one. If you can safely assume you always need to change the last ., you could do:

$ echo 'version:' | sed 's/\.\([^.]*\)$/-\1/'
version: 1.8.0-110

or, if your sed supports -E (most do) with:

$ echo 'version:' | sed -E 's/\.([^.]*)$/-\1/'
version: 1.8.0-110

That will look for a . and then 0 or more non-. characters ([^.]) until the end of the line ($). Because the [^.]* is in parentheses (\( \)), it is "captured" and can be referred to as \1 on the right hand side of the replacement operator. So this will replace the . and the non-. characters after it with a - and those characters.

If it must be the third and not the last, @Ned64 already gave the simplest approach, but as an alternative, you could do:

$ echo 'version:' | perl -pe 's/(([^.]+\.){2}[^.]+)\./$1-/'
version: 1.8.0-110
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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