tr 使用范围的奇怪行为

Chr*_*ris 11 command-line shell wildcards tr

我有一台特定的服务器在使用 tr 时表现出奇怪的行为。这是来自工作服务器的示例:

-bash-3.2$ echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
-bash-3.2$
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义。

然而,这是来自“特殊”服务器:

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890
Run Code Online (Sandbox Code Playgroud)

如您所见,删除所有小写字符失败。但是,它已经删除了字母“o”

有趣的部分是以下两个示例,它们对我来说毫无意义:

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-n]
opqrstuvwxyz1234567890
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-o]
abcdefghijklmnpqrstuvwxyz1234567890
[root@host~]#
Run Code Online (Sandbox Code Playgroud)

(同样,'o' 在最后一个例子中被删除了)

有谁知道这里发生了什么?我无法在我正在使用的任何其他 linux 机器上复制。

Arc*_*mar 25

o在当前目录中有一个名为的文件

foo> ls
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
foo> touch o
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890
Run Code Online (Sandbox Code Playgroud)

[a-z]如果找到匹配项,shell 将展开字符串。

这称为路径名扩展,根据 man bash

路径名扩展
分词后,除非设置了 -f 选项,否则 bash 会扫描每个单词中的字符 *、? 和 [。……(……)

bash 将执行扩展。

[...] 匹配任何一个封闭的字符。


Gil*_*il' 8

怎么了

shell (bash) 看到参数[a-z]. 这是一个通配符模式(一个glob),它匹配任何小写字母¹。因此,shell 会查找与此模式匹配的文件名。有以下三种情况:

  • 当前目录中没有文件的名称是单个小写字母。然后 shell 保持通配符模式不变,并tr查看参数-d[a-z]。这是大多数机器上发生的情况。
  • 当前目录中的单个文件的名称是单个小写字母。然后 shell 将模式扩展到这个文件名,并tr查看参数-d和文件名。这发生在服务器上,并且匹配文件被调用,o因为我们可以看到tr删除了字母o.
  • 当前目录中的两个或多个文件的名称为单个小写字母。然后 shell 将模式扩展到匹配文件名的列表,并tr看到三个或更多参数:-d和文件名。由于tr期望在 之后有一个参数-d,它会抱怨。

你应该做的

如果命令的参数中有特殊字符,则必须对它们进行转义。将参数放在单引号中'…'(这是最简单的方法,还有其他方法)。在单引号内,除了单引号本身之外,所有字符都代表自己。如果参数中有单引号,请将其替换为'\''

tr -d '[a-z]'
Run Code Online (Sandbox Code Playgroud)

但是请注意,这可能仍然不是您的意思!这告诉tr删除小写字母和方括号。这相当于tr -d ']a-z['tr '[]a-z'等要删除小写字母,使用

tr -d a-z
Run Code Online (Sandbox Code Playgroud)

的参数tr是一个字符集。您在正则表达式或通配符模式中的字符集周围放置方括号以指示它是一个字符集。但tr一次只处理一个字符。它的命令行参数是您放在括号内的内容

您确实需要括号来表示字符类。在正则表达式中,您使用方括号内的方括号来表示字符类,例如[[:lower:]]*匹配任意数量的小写字母、[[:lower:]_]*匹配任意数量的小写字母和下划线。在 的参数中tr,您需要不带括号的集合,因此tr -d '[:lower:]'删除小写字母,tr -d '[:lower:]_'删除小写字母和下划线等。

¹在某些语言环境中,它可能与其他字符匹配

  • 请注意,在 Solaris 10(和其他古老的基于 SysV 的 Unices)上,您确实需要使用 `/usr/bin/tr` 和 `tr -d '[az]'`。使用`/usr/xpg4/bin/tr`,`tr -d az` 有效,但`tr -d '[az]'` 不会删除`[` 或`]`。 (2认同)
  • `/usr/xpg4/bin/tr -d '[az]'` 不删除 `[` 或 `]` 显然已在 Solaris 11 中修复。 (2认同)