What is happening
The shell (bash) sees the argument [a-z]
. That's a wildcard pattern (a glob), which matches any lowercase letter¹. Therefore the shell looks for a file name that matches this pattern. There are three cases:
- No file in the current directory has a name that is a single lowercase letter. Then the shell leaves the wildcard pattern unchanged, and
tr
sees the arguments -d
and [a-z]
. This is what happens on most of your machines.
- A single file in the current directory has a name that is a single lowercase letter. Then the shell expands the pattern to this file name, and
tr
sees the arguments -d
and the file name. This happens on the server, and the matching file is called o
since we can see that tr
deleted the letter o
.
- Two or more files in the current directory have a name that is a single lowercase letter. Then the shell expands the pattern to the list of matching file names, and
tr
sees three or more arguments: -d
and the file names. Since tr
expects a single argument after -d
, it will complain.
What you should have done
If there are special characters in the argument of a command, you must escape them. Put the argument in single quotes '…'
(this is the simplest way, there are others). Inside single quotes, all characters stand for themselves except the single quote itself. If there is a single quote inside the argument, replace it by '\''
.
tr -d '[a-z]'
However note that this is still probably not what you meant! This tells tr
to delete lowercase letters and square brackets. It's equivalent to tr -d ']a-z['
, tr '[]a-z'
, etc. To delete lowercase letters, use
tr -d a-z
The argument to tr
is a character set. You put brackets around a character set in a regular expression or wildcard pattern to indicate that it's a character set. But tr
works on a single character at a time. Its command line arguments are what you'd put inside the brackets.
You do need brackets to indicate character classes. In a regular expression, you use brackets inside brackets to indicate a character class, e.g. [[:lower:]]*
matches any number of lowercase letters, [[:lower:]_]*
matches any number of lowercase letters and underscores. In the argument of tr
, you need the set without its surrounding brackets, so tr -d '[:lower:]'
deletes lowercase letters, tr -d '[:lower:]_'
deletes lowercase letters and underscores, etc.
¹ In some locales it may match other characters.
tr
ranges are written without the enclosing[...]
. Sotr -d '[a-z]'
will killa-z
, and also characters[
and]
. Usetr -d a-z
to kill only lettersa-z
. – Satō Katsura Jul 03 '18 at 11:21