How can I increment a number at the end of a string in bash?

updateVersion()
{
    [[ $1 =~ ([^0-9]*)([0-9]+) ]] || { echo 'invalid input'; exit; }     
    echo "${BASH_REMATCH[1]}$(( ${BASH_REMATCH[2]} + 1 ))"
}

# Usage
updateVersion version_11         # output: version_12
updateVersion version11          # output: version12
updateVersion something_else123  # output: something_else124
updateVersion "with spaces 99"   # output: with spaces 100

# Putting it in a variable
v2="$(updateVersion version2)"
echo "$v2"                       # output: version3

Late to the party here, but there is an issue with the accepted answer. It works for the OP's case where there are no numbers before the end, but I had an example like this:

1.0.143

For that, the regexp needs to be a bit looser. Here's how I did it, preserving leading zeroes:

#!/usr/bin/env bash

updateVersion()
{
  [[ ${1} =~ ^(.*[^0-9])?([0-9]+)$ ]]  && \
    [[ ${#BASH_REMATCH[1]} -gt 0 ]] && \
      printf "%s%0${#BASH_REMATCH[2]}d" "${BASH_REMATCH[1]}" "$((10#${BASH_REMATCH[2]} + 1 ))" || \
      printf "%0${#BASH_REMATCH[2]}d" "$((10#${BASH_REMATCH[2]} + 1))" || \
    printf "${1}"
}

# Usage
updateVersion 09          # output 10
updateVersion 1.0.450     # output 1.0.451
updateVersion version_01  # output version_02
updateVersion version12   # output version13
updateVersion version19   # output version20

Notes:

  1. You only need to double-quote the first argument to printf.
  2. Replace ${1} with content in "" if you want to use it on a command line, instead of in a function.
  3. You can switch the last printf to a basic echo if you prefer. If you are just printing to stdout or stderr, consider adding a newline (\n) at the end of each printf.
  4. You can combine the function content into a single line, but it's harder to read. It's better to break it into lines with \ at every if (&&) and else (||), as above.

What the function does - line by line:

  • Test the passed value ends with a number of one or more digits, optionally prefixed by at least one non-number. Split into two groups accordingly (indexing is 1-based).
  • When ending in a number, test there is a non-numeric prefix (i.e. length of group 1 > 0).
  • When there are non-numerics, print group 1 (a string) followed by group 2 (an integer padded with zeroes to match the original string size). Group 2 is base-10 converted and incremented by 1. The conversion is important - leading zeroes are interpreted as octal by default.
  • When there are only numbers, increment as above but just print group 2.
  • If the input is anything else, return the supplied string.

incrementTrailingNumber() {
  local prefix number
  if [[ $1 =~ ^(.*[^[:digit:]])([[:digit:]]+)$ ]]; then
    prefix=${BASH_REMATCH[1]}
    number=${BASH_REMATCH[2]}
    printf '%s%s\n' "$prefix" "$(( number + 1 ))"
  else
    printf '%s\n' "$1"
  fi
}

Usage as:

$ incrementTrailingNumber version_30
version_31
$ incrementTrailingNumber foo-2.15
foo-2.16
$ incrementTrailingNumber noNumberHereAtAll  # demonstrate noop case
noNumberHereAtAll

Tags:

Bash