Make tr replace only exact characters given in arguments

Viewed 40

I'm trying to convert filenames to remove unacceptable characters, but tr doesn't always treat its input arguments exactly as they're given.

For example:

$ echo "(hello) - {world}" | tr '()-{}' '_'
_______ _ _______

...whereas I only intended to replace (, ), -, { and }, all the characters between ) and { in ASCII collation order were replaced as well -- so every letter in the input also became a _!

Is there a way to make tr replace only the exact characters given in its argument?

1 Answers

tr's syntax is surprisingly complicated. It supports ranges, character classes, collation-based equivalence matching, etc.

To avoid surprises (when a string matches any of that syntax unexpectedly), we can convert our literal characters to a string of \### octal specifiers of those characters' ordinals:

trExpressionFor() {
  printf %s "$1" | od -v -A n -b | tr ' ' '\\'
}

trL() { # name short for "tr-literal"
  tr "$(trExpressionFor "$1")" "$(trExpressionFor "$2")"
}

...used as:

$ trExpressionFor '()-{}'
\050\051\055\173\175
$ echo "(hello) - {world}" | trL '()-{}' '_'
_hello_ _ _world_
Related