tr命令处理|、\等特殊字符时的异常行为及解决方法
|和\的特殊处理逻辑 先确认你的场景和可行命令:
原始test.txt内容:
+-foo.bar:2.4 | bar.foo:1.1:test \| hello.goobye:3.3.3 \|+- baz.yeah:4
你找到的可行命令:cat test.txt | tr -d "[:blank:]|\\\+-"
预期输出:foo.bar:2.4bar.foo:1.1:testhellogoobye:3.3.3baz.yeah:4
接下来拆解|和\的处理细节:
1. 关于|的处理
在shell环境中,|是特殊的管道操作符——它的作用是把前一个命令的输出传给后一个命令。如果不在引号中包裹或者不用反斜杠转义,shell会把|解析成管道,而不是作为普通字符传给tr。
比如如果你直接写tr -d |,shell会认为你要把tr -d的输出传给下一个命令(但后面没有命令,会直接报错)。而你把|放在双引号"..."里,shell就会把它当作普通字符,直接传递给tr。在tr的字符集中,|本身没有特殊含义,就是一个普通的待删除字符,所以tr会正确移除所有|。
2. 关于\的处理
这个要分两层来看:
第一层:shell的转义规则
\在shell中是转义字符,不管是单引号还是双引号,它都有特殊作用(双引号中\可以转义$、`、"和自身)。如果你想让shell把一个字面的\传递给tr,必须用\\来表示——第一个\转义第二个\,最终shell会把\\转换成一个\传给tr。
第二层:tr的字符集规则
在tr中,\本身也不是特殊字符,只要shell正确把它传递给tr,tr就会把它当作待删除的普通字符。
那你命令里的\\\+是什么意思?其实分解开看:
\\:shell解析后变成一个\,传给tr\+:在双引号中,\转义+,但+在shell中不是特殊字符,所以最终就是+传给tr- 最后把
-放在字符集的末尾,是因为tr中如果-出现在字符集中间,会被当作范围符号(比如a-z表示从a到z的所有字符),放在末尾就会被当作普通的-字符处理。
简化你的命令(可选)
其实你可以把命令简化成更易读的形式:
cat test.txt | tr -d '[:blank:]|\+-'
用单引号包裹字符集的话,shell不会对里面的任何字符做转义处理(除了单引号本身),所以\、|、+、-都可以直接写,更直观。
内容的提问来源于stack exchange,提问作者Cody Patterson

