为什么命令 tr "\'\\\"\?\!" "01234" 不起作用?

Rob*_*b U 8 special-characters tr

在终端中,如果我定义一些变量 char 如下:

export char=\'\\\"\?\!
Run Code Online (Sandbox Code Playgroud)

实际上,char 是字符串

'\"?!
Run Code Online (Sandbox Code Playgroud)

然后我使用tr命令替换'\"?!为数字01234

tr "\'\\\"\?\!" "01234"
Run Code Online (Sandbox Code Playgroud)

我以为我会得到

01234
Run Code Online (Sandbox Code Playgroud)

相反,我得到了

0\123
Run Code Online (Sandbox Code Playgroud)

如果有人能向我解释发生了什么,我将不胜感激。

似乎用sed命令单独替换每个字符可以避免这个问题,但为什么呢?

egm*_*ont 17

不仅仅是外壳,而且tr它本身也将反斜杠解释为特殊的转义字符,有关详细信息,请参阅其手册。因此,当您要替换反斜杠时,您需要确保tr收到文字\\(两个反斜杠)。这可以通过例如char=...\\\\...在 shell 中完成,这部分不需要进一步解释,因为您正确理解 shell 如何处理反斜杠。

这对您来说可能不方便,但在许多其他情况下很方便,并且允许字符集或 NUL 字节成为搜索或替换集的一部分(否则不可能)。例如,要将以 NUL 分隔的字符串转换为以换行符分隔的字符串,您可以执行类似的操作tr '\0' '\n' < /proc/1234/environ,或者将字符串使用小写tr '[:upper:]' '[:lower:]'。如果tr没有转义字符,这些将是不可能的。


Ed *_*ton 5

'除非您需要使用双引号 ( ") 使 shell 解释它,否则始终将字符串和脚本括在单引号 ( ) 中。请参阅https://mywiki.wooledge.org/Quotes。通过使用双引号,您是在邀请 shell 进入,因此将自己置于“转义”地狱中,您必须先转义字符串中的字符以供 shell 使用,然后再次转义它们以供工具使用,因此您需要添加多个转义层而不是 1. 只是不要这样做,而是使用单引号:

$ printf '%s\n' ''\''\"?!'
'\"?!

$ printf '%s\n' ''\''\"?!' | tr ''\''\\"?!' '01234'
01234
Run Code Online (Sandbox Code Playgroud)

定义变量时也是如此char。而不是所有这些反斜杠:

export char=\'\\\"\?\!
Run Code Online (Sandbox Code Playgroud)

只需正确单引号字符串:

$ export char=''\''\"?!'

$ printf '%s\n' "$char"
'\"?!

$ printf '%s\n' "$char" | tr ''\''\\"?!' '01234'
01234
Run Code Online (Sandbox Code Playgroud)

在上述所有你需要知道的是,获得'一个内'壳-enclosed字符串'\'',你需要转义反斜线的tr,这么tr知道把它当作一个反斜杠而不是随后逃跑"

  • 虽然我通常赞同在可能的情况下使用单引号的规则,但记住魔法调用 `'\''` 的需要并不是真的想避免“逃离地狱”。 (2认同)
  • @IMSoP,哦,确实如此。但是在这里,带有各种特殊字符的字符串,包括引号和反斜杠……好吧,无论如何它都会看起来很糟糕。(除非你有像 Perl 的 `q('\"?!)` 这样的东西,你可以在其中选择字符串中不需要的分隔符) (2认同)