Search code examples
bashshellpattern-matchingsubstringvariable-expansion

Replace Last Occurrence of Substring in String (bash)


From the bash software manual:

${parameter/pattern/string}

The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string.
... If pattern begins with ‘%’, it must match at the end of the expanded value of parameter.

And so I've tried:

local new_name=${file/%old/new}

Where string is an absolute file path (/abc/defg/hij and old and new are variable strings.

However this seems to be trying to match the literal %sb1.

What is the syntax for this?

Expected Output:

Given

old=sb1
new=sb2

Then

/foo/sb1/foo/bar/sb1 should become /foo/sb1/foo/bar/sb2

/foo/foosb1other/foo/bar/foosb1bar should become /foo/foosb1other/foo/bar/foosb2bar


Solution

  • Using only shell-builtin parameter expansion:

    src=sb1; dest=sb2
    old=/foo/foosb1other/foo/bar/foosb1bar
    
    if [[ $old = *"$src"* ]]; then
      prefix=${old%"$src"*}                  # Extract content before the last instance
      suffix=${old#"$prefix"}                # Extract content *after* our prefix
      new=${prefix}${suffix/"$src"/"$dest"}  # Append unmodified prefix w/ suffix w/ replacement
    else
      new=$old
    fi
    
    declare -p new >&2
    

    ...properly emits:

    declare -- new="/foo/foosb1other/foo/bar/foosb2bar"