2

I'm struggling a bit with this replacement regex, I could not understand why it is behaving this way. What I want to do is to replace all numeric digits after the last /:

str="aa/a0b0/42"
echo ${str/%[[:digit:]]*/5}
echo ${str/%[0-9]*/5}

Both echo's produce aa/a5 as output. The expected output would be aa/a0b0/5.

I'm well aware that I could use tools such as sed and awk to solve my problem, and I do know how to do it, but I'm looking for a bash-only solution.

I would like to understand why the [0-9]* is expanding to 0b0/42 instead of only numeric digits.

The % in my regex is to make back end replacement.

5

Because that's a pattern matching, not a regular expression.

[[:digit:]]* means one digit follow by any string, that's why 0b0/42 matched. It's one digit, 0, follow by a string, b0/42.

Now, in bash, with extglob enabled, you can use +(pattern-list) to match one or more occurrences of a pattern:

$ shopt -s extglob
$ printf '%s\n' "${str/%+([[:digit:]])/5}"
aa/a0b0/5

You have that syntax by default in ksh, which bash copied from.

In zsh, while you could use those ksh patterns with setopt kshglob or use zsh own extendedglob ([[:digit:]]##), you can also perform regex replace with the regex-replace function:

$ str="aa/a0b0/42"
$ autoload -U regexp-replace
$ regexp-replace str '[[:digit:]]+$' 5
$ print -rl -- $str
aa/a0b0/5

If you ensure that the part after the last / always numbers, you can do it POSIXly:

printf '%s/%s\n' "${str%/*}" 5

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.