我有一台特定的服务器在使用 tr 时表现出奇怪的行为。这是来自工作服务器的示例:
-bash-3.2$ echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
-bash-3.2$
这对我来说非常有意义。
然而,这是来自“特殊”服务器:
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890
如您所见,删除所有小写字符失败。但是,它删除了字母“o”
有趣的部分是以下两个例子,这对我来说毫无意义:
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-n]
opqrstuvwxyz1234567890
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-o]
abcdefghijklmnpqrstuvwxyz1234567890
[root@host~]#
(同样,在最后一个示例中删除了“o”)
有谁知道这里发生了什么事吗?我无法在我正在使用的任何其他 Linux 机器上重现。
答案1
o
您在当前目录中有一个名为的文件
foo> ls
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
foo> touch o
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890
[a-z]
如果找到匹配项,shell 将扩展字符串。
这称为路径名扩展,根据man bash
路径名扩展
分词后,除非设置了 -f 选项,否则 bash 会扫描每个单词中的字符 *、? 和 [。 ... (...)
bash 将执行扩展。
[...] 匹配任何一个包含的字符。
答案2
怎么了
shell (bash) 看到参数[a-z]
。这是一个通配符模式(a全局),它匹配任何小写字母。因此,shell 会查找与此模式匹配的文件名。分三种情况:
- 当前目录中没有文件的名称是单个小写字母。然后 shell 保持通配符模式不变,并
tr
查看参数-d
和[a-z]
。大多数机器上都会发生这种情况。 - 当前目录中的单个文件的名称为单个小写字母。然后 shell 将模式扩展为该文件名,并
tr
查看参数-d
和文件名。这发生在服务器上,并且调用匹配的文件,o
因为我们可以看到tr
删除了字母o
。 - 当前目录中的两个或多个文件的名称为单个小写字母。然后 shell 将模式扩展为匹配文件名列表,并
tr
看到三个或更多参数:-d
和 文件名。由于tr
期望在 后有一个参数-d
,所以它会抱怨。
你应该做什么
如果命令的参数中有特殊字符,则必须对它们进行转义。将参数放在单引号中'…'
(这是最简单的方法,还有其他方法)。在单引号内,除了单引号本身之外,所有字符均代表其自身。如果参数中有单引号,将其替换为'\''
。
tr -d '[a-z]'
但请注意,这可能仍然不是您的意思!这告诉tr
删除小写字母和方括号。它相当于tr -d ']a-z['
,tr '[]a-z'
等。要删除小写字母,请使用
tr -d a-z
的参数tr
是字符集。您可以在正则表达式或通配符模式中将字符集放在括号内,以指示它是字符集。但tr
一次只作用于一个角色。它的命令行参数是你在括号内写的内容。
您确实需要括号来表示字符类。在正则表达式中,您可以使用括号内的括号来指示字符类,例如,[[:lower:]]*
匹配任意数量的小写字母、[[:lower:]_]*
匹配任意数量的小写字母和下划线。在 的参数中tr
,您需要不带括号的集合,因此tr -d '[:lower:]'
删除小写字母、tr -d '[:lower:]_'
删除小写字母和下划线等。