0

I have a string in a common pattern that I want to manipulate. I want to be able to turn string 5B299 into 5B300 (increment the last number by one).

I want to avoid blindly splicing the string by index, as the first number and letter can change in size. Essentially I want to be able to get the entire value of everything after the first character, increment it by one, and re-append it.

The only things I've found online so far show me how to cut by a delimiter, but I don't have a constant delimiter.

Ricky
  • 3,101
  • 1
  • 25
  • 33
  • 1
    What did you try? Post those failed attempts to the question. Are you claiming only the last 3 characters should be considered? – Inian May 20 '19 at 05:15
  • @Inian I have no failed attempts, I'm too new to the command line to have tried anything for myself yet. It's not exactly three numbers, it could be one number or five numbers, depending on how many times it is incremented. – Ricky May 20 '19 at 05:17
  • Please don't make us guess about all the details of your sample input, provide a reasonable (but small) set of samples. Good luck. – shellter May 20 '19 at 12:12

3 Answers3

2

You could use the regex features supported by the bash shell with its ~ construct that supports basic Extended Regular Expression matching (ERE). All you need to do is define a regex and work on the captured groups to get the resulting string

str=5B299
re='^(.*[A-Z])([0-9]+)$' 

Now use the ~ operator to do the regex match. The ~ operator populates an array BASH_REMATCH with the captured groups if regex match was successful. The first part (5B in the example) would be stored in the index 0 and the next one at 1. We increment the value at index 1 with the $((..)) operator.

if [[ $str =~ $re ]]; then
    result="${BASH_REMATCH[1]}$(( BASH_REMATCH[2] + 1 ))"
    printf '%s\n' "$result"
fi

The POSIX version of the regex, free of the locale dependency would be to use character classes instead of range expressions as

posix_re='^(.*[[:alpha:]])([[:digit:]]+)$'
Inian
  • 80,270
  • 14
  • 142
  • 161
2

You can do what you are attempting fairly easily with the bash parameter-expansion for string indexes along with the POSIX arithmetic operator. For instance you could do:

#!/bin/bash

[ -z "$1" ] && {    ## validate at least 1 argument provided
    printf "error: please provide a number.\n" >&2
    exit 1
}

[[ $1 =~ [^0-9][^0-9]* ]] && {  ## validate all digits in argument
    printf "error: input contains non-digit characters.\n" >&2
    exit 1
}

suffix=${1:1}           ## take all character past 1st as suffix
suffix=$((suffix + 1))  ## increment suffix by 1

result=${1:0:1}$suffix  ## append suffent to orginal 1st character
echo "$result"          ## output

exit 0

Which will leave the 1st character alone while incrementing the remaining characters by 1 and then joining again with the original 1st digit, while validating that the input consisted only of digits, e.g.

Example Use/Output

$ bash prefixsuffix.sh
error: please provide a number.

$ bash prefixsuffix.sh 38a900
error: input contains non-digit characters.

$ bash prefixsuffix.sh 38900
38901

$ bash prefixsuffix.sh 39999
310000

Look things over and let me know if that is what you intended.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

You can use sed in conjunction with awk:

increment() {
   echo $1 | sed -r 's/([0-9]+[a-zA-Z]+)([0-9]+)/\1 \2/' | awk '{printf "%s%d", $1, ++$2}'
}

echo $(increment "5B299")
echo $(increment "127ABC385")
echo $(increment "7cf999")

Output:

5B300
127ABC386
7cf1000
constt
  • 2,250
  • 1
  • 17
  • 18
  • I'm getting the error `sed: illegal option -- r`, running Bash 3.2.57 on macOS 10.15. – Ricky May 20 '19 at 06:31
  • @Ricky You need `gnu-sed` installed on your system https://stackoverflow.com/a/46859893/3920623 – constt May 20 '19 at 07:27
  • 1
    Oh, it seems there is a simpler solution for Mac, just change `-r` option to `-E` and it should work. – constt May 20 '19 at 07:45