This just happened to me in bash:
$ echo HELLO|tr [:upper:] [:lower:]
hello
$ touch w
$ echo HELLO|tr [:upper:] [:lower:]
wwwww
$ echo [:upper:]
[:upper:]
$
An expression like [:lower:]
is a glob pattern if you don't enclose it in quotes. Thus, it matches a filename that is exactly one* of the characters in the square braces. What you probably want is to simply quote it:
$ tr '[:upper:]' '[:lower:]' <<< ABC
abc
As hobbs points out in his comment, I should explain the inconsistency of why the behavior changes depending on if the file is present or not. If no file matching the glob exists, the behavior of bash is determined by whether or not the nullglob
extension is enabled. It's disabled by default.
If no matching file names are found, and the shell option nullglob is not enabled, the word is left unchanged.
Thus, if no file with a name consisting entirely of one of ':', 'l', 'o', 'w', 'e', or 'r' exists, the glob [:lower:]
will expand to the word [:lower:]
.
*Updated. As Glenn Jackman points out in his comment, I had previously mistakenly confused [:lower:]
for [[:lower:]]
. It is confusing. In most places where you apply regular expressions in POSIX utilities and shell commands the character class itself has to be explicitly wrapped with square braces. So [a-z]
is [[:lower:]]
in the C
locale, for example. But tr
breaks the rules and implies the square braces, such that tr -d '[:lower:]'
is equivalent to tr -d 'a-z'
in the C
locale. So my excuse is that when tr
is involved I regularly forget how many braces are actually needed. :P