As RedGrittyBrick said, the best way to do it is using an XML parser, picking out the element, translate characters and then write it back using an XML library. This will not give you nasty surprises, it will stand the test of time, etc. It is not only best, it is far superior to other things. Other solutions more or less instantly become nightmares to debug, and there will certainly be hidden problems more or less everywhere.
If it's just a simple task that needs to be done once, and one is very careful, and one checks the result, etc., etc., etc., then it might be less work to do it the bad way. But it will surprise you some day if you make it a habit.
As example, here is one of the bad ways that seem to work, but it relies not just on valid XML, but the more or less exact syntax you described earlier, which is just a subset of valid XML, and thus valid XML is certainly able to make the code fail (what if someone adds a '>' sign in one of the tags? Add a special case. What if someone doesn't use quotation marks? Add a special case, and so on). This is the problem of not using a real parser. Some care has been taken below to act like a pseudoparser at least, reading the tag, then acting on it, then writing it back, but there are ready tools for this that have been tested extensively.
#!/bin/sh
IFS='\n'
while read i; do
if $(printf -- "${i}" | grep -qE '<tree [^>]+ bar="[^'"${1}"'"]*'"${1}"); then
ORIGTAG=$(printf -- "${i}" | sed 's#^.*<tree [^>]\+ bar="\([^"]\+\)".*$#\1#g')
NEWTAG=$(printf -- "${ORIGTAG}" | tr "${1}" "${2}")
printf -- "${i}\n" | sed 's#\(^.*<tree [^>]\+ bar="\)'"${ORIGTAG}"'\(".*$\)#\1'"${NEWTAG}"'\2#g'
else
printf -- "${i}\n"
fi
done < "${3}"
Usage: script.sh [character to replace] [replacing character] [filename], e.g.
script.sh c X myfile
IFS sets the "internal field separator" in the shell to newline, to keep whitespace in the beginning of the lines.
while read reads the input file (given as argument 3 to the script) line by line.
grep checks if the specific tag is in the current line AND if the tag contains the character to be translated. If so, go to sed logic; if not, return the line as-is.
sed picks out the old tag, runs a character translation on it and returns the line with the new tag.
As you can see, no one would like to find this script and have to debug it. If this is anything else than a one-off job, don't do it like this. For the sanity of future observers.