2.6 用tr进行转换

tr是UNIX命令行行家工具箱中一件精美的小工具。它经常用来编写优美的单行命令,作用不容小视。

tr可以对来自标准输入的字符进行替换、删除以及压缩。它可以将一组字符变成另一组字符,因而通常也被称为转换(translate)命令。

2.6.1 预备知识

tr只能通过stdin(标准输入),而无法通过命令行参数来接受输入。它的调用格式如下:

  1. tr [options] set1 set2

将来自stdin的输入字符从set1映射到set2,并将其输出写入stdout(标准输出)。set1set2是字符类或字符集。如果两个字符集的长度不相等,那么set2会不断重复其最后一个字符,直到长度与set1相同。如果set2的长度大于set1,那么在set2中超出set1长度的那部分字符则全部被忽略。

2.6.2 实战演练

将输入字符由大写转换成小写,可以使用下面的命令:

  1. $ echo "HELLO WHO IS THIS" | tr 'A-Z' 'a-z'

'A-Z''a-z'都是集合。我们可以按照需要追加字符或字符类来构造自己定制的集合。

'ABD-}''aA.,''a-ce-x'以及'a-c0-9'等均是合法的集合。定义集合也很简单,不需要去书写一长串连续的字符序列,相反,我们可以使用“起始字符-终止字符”这种格式。这种写法也可以和其他字符或字符类结合使用。如果“起始字符-终止字符”不是一个连续的字符序列,那么它就会被视为一个包含了三个元素的集合(“起始字符-终止字符”)。你可以使用像'\t''\n'这种特殊字符,也可以使用其他ASCII字符。

2.6.3 工作原理

通过在tr中使用集合的概念,我们可以轻松地将字符从一个集合映射到另一个集合中。让我们通过一则示例看看如何用tr进行数字加密和解密。

  1. $ echo 12345 | tr '0-9' '9876543210'
  2. 87654 #已加密
  3.  
  4. $ echo 87654 | tr '9876543210' '0-9'
  5. 12345 #已解密

再来看另外一个有趣的例子。

ROT13是一个著名的加密算法。在ROT13算法中,文本加密和解密都使用同一个函数。ROT13按照字母表排列顺序执行13个字母的转换。用tr进行ROT13加密:

  1. $ echo "tr came, tr saw, tr conquered." | tr
  2. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  3. 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'

得到输出:

  1. ge pnzr, ge fnj, ge pbadhrerq.

对加密后的密文再次使用同样的ROT13函数,我们采用:

  1. $ echo ge pnzr, ge fnj, ge pbadhrerq. | tr
  2. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  3. 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'

于是,得到输出:

  1. tr came, tr saw, tr conquered.

tr还可以用来将制表符转换成空格:

  1. $ cat text | tr '\t' ' '

2.6.4 补充内容

  • tr删除字符

tr有一个选项-d,可以通过指定需要被删除的字符集合,将出现在stdin中的特定字符清除掉:

  1. $ cat file.txt | tr -d '[set1]'
  2. #不要set1

例如:

  1. $ echo "Hello 123 world 456" | tr -d '0-9'
  2. Hello world
  3. # 将stdin中的数字删除并打印出来
  • 字符集补集

我们可以利用选项-c来使用set1的补集。-c [set]等同于定义了一个集合(补集),这个集合中的字符不包含在[set]中:

  1. tr -c [set1] [set2]

set1的补集意味着这个集合中包含set1中没有的所有字符。

最典型的用法是从输入文本中将不在补集中的所有字符全部删除。例如:

  1. $ echo hello 1 char 2 next 4 | tr -d -c '0-9 \n'
  2. 1 2 4

在这里,补集中包含了除数字、空格字符和换行符之外的所有字符。因为指定了-d,所以这些字符全部都被删除。

  • tr压缩字符

tr命令在很多文本处理环境中相当有用。多数情况下,连续的重复字符应该被压缩成单个字符,而经常需要进行的一项任务就是压缩空白字符。

tr-s选项可以压缩输入中重复的字符,方法如下:

  1. $ echo "GNU is not UNIX. Recursive right ?" | tr -s ' '
  2. GNU is not UNIX. Recursive right ?
  3. # tr -s '[set]'

让我们用一种巧妙的方式用tr将文件中的数字列表进行相加:

  1. $ cat sum.txt
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. $ cat sum.txt | echo $[ $(tr '\n' '+' ) 0 ]
  8. 15

这一招是如何起效的?

在上面的命令中,tr用来将'\n'替换成'+',因此我们得到了字符串"1+2+3+…5+",但是在字符串的尾部多了一个操作符+。为了抵消这个多出来的操作符,我们再追加一个0。

$[ operation ]执行算术运算,因此得到下面的字符串:

  1. echo $[ 1+2+3+4+5+0 ]

如果我们利用循环从文件中读取数字,然后再进行相加,那肯定得用好几行代码。如今我们只用一行就完成任务了。这种编写单行命令的技巧可通过实践才能习得。

  • 字符类

tr可以像使用集合一样使用各种不同的字符类,这些字符类如下所示。

  • alnum:字母和数字。

  • alpha:字母。

  • cntrl:控制(非打印)字符。

  • digit:数字。

  • graph:图形字符。

  • lower:小写字母。

  • print:可打印字符。

  • punct:标点符号。

  • space:空白字符。

  • upper:大写字母。

  • xdigit:十六进制字符。

可以按照下面的方式选择并使用所需的字符类:

  1. tr [:class:] [:class:]

例如:

  1. tr '[:lower:]' '[:upper:]'