如何检查 bash 命令字符串的逐字字符?

Gab*_*enn 15 linux bash gnome-terminal

今天早上我在 bash 终端中有这种奇怪的行为:

user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
bash: [: missing «]»
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
true
Run Code Online (Sandbox Code Playgroud)
  • 第一个命令是从使用 gedit 编辑的脚本粘贴的
  • 第二个是直接在终端中输入的。

经过一番挖掘,我发现删除第 30 个字符(client.conf 和“]”之间的空格)并用空格替换它使命令再次起作用。

我的假设是正确的:命令中出现了一个未知的空白字符,但问题是:

  1. 如何在终端中显示这些字符以便调试命令?更重要的是:
  2. 我怎样才能防止这种情况再次发生?

顺便说一句,我运行的是 Ubuntu 18.04 / 法语,我粘贴命令的脚本位于 USB 驱动器中,并且也可能在 Windows 上进行了编辑。


谢谢你的好答案。坏字符是一个c2 a0不间断空格 UTF-8 字符。How to remove special 'M-BM-' character with sed这个问题有关于该角色的有趣事实。

奇怪的是,剧本竟然没有这个角色。所以我不知道它是从哪里来的。

Kiw*_*iwy 18

您可以使用cat以下-A选项:来自手册:

   -A, --show-all
          equivalent to -vET
   -E, --show-ends
          display $ at end of each line
   -T, --show-tabs
          display TAB characters as ^I
   -v, --show-nonprinting
          use ^ and M- notation, except for LFD and TAB
Run Code Online (Sandbox Code Playgroud)

所以cat -A yourscrip.sh会向你展示隐形和奇怪的字符。

  • 此解决方案有效:`echo "[ -f /etc/openvpn.ovpn ]" | cat -A` 返回 `[ -f /etc/openvpn/client.ovpnM-BM- ]$`。我们可以看到 *M-BM-* UT-8 字符_非换行空格_ (7认同)

Att*_*tie 11

一种选择是使用十六进制查看器或编辑器查看您尝试使用的字符。hexdump如果您仅限于终端,这是一个不错的选择。

$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000  5b 20 2d 66 20 2f 65 74  63 2f 6f 70 65 6e 76 70  |[ -f /etc/openvp|
00000010  6e 2f 63 6c 69 65 6e 74  2e 63 6f 6e 66 20 5d 20  |n/client.conf ] |
00000020  26 26 20 65 63 68 6f 20  74 72 75 65 0a           |&& echo true.|
0000002d
Run Code Online (Sandbox Code Playgroud)

您可以在这里看到space, close-square-brace,space是正确的 - 0x20, 0x5D, 0x20

这些值是 ASCII 代码,以十六进制显示。范围之外的任何值0x20-0x7E就ASCII 而言,都不是可打印字符,并且很可能无法很好地与命令行界面配合使用。

注意:我复制了您的第一个“”行以用于hexdump上面的示例,因此某些内容已将非 ASCII 空间替换为原始源和呈现的问题之间的 ASCII 空间。


要重复此操作,请执行以下步骤:

  1. 输入hexdump -Cv <<"EOF"并按下Enter
  2. 粘贴您要使用的文本
  3. 键入EOF一个属于自己的行,然后按Enter

终端和命令行界面不能很好地处理特殊字符 - 正如您所发现的。如果您在格式化文档时不是很小心,那么使用“智能引号”、长破折号的Microsoft Word(和其他人)也会遇到问题,列表还在继续……

找出区别:(上边是“智能引号”,下边是“直引号”)

智能引号与直引号的示例

$ hexdump -Cv <<"EOF"
> “quoted string”
> EOF
00000000  e2 80 9c 71 75 6f 74 65  64 20 73 74 72 69 6e 67  |...quoted string|
00000010  e2 80 9d 0a                                       |....|
00000014
Run Code Online (Sandbox Code Playgroud)

在这里,左引号不是简单的 ASCII 引号 ( "),而是一个 Unicode / UTF-8系列 - 0xE20x800x9C、 或U+201C- 终端不会像您预期的那样处理。

Kiwy 的建议cat -A也可以完成这项工作:

$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$
Run Code Online (Sandbox Code Playgroud)

注意:使用 时echo "..." | hd,您有可能 bash 会替换您尝试检查的部分字符串。在尝试检查脚本的组件时,这尤其令人担忧。

例如尝试:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

$ cat <<EOF
> ${USER}
> EOF
attie
Run Code Online (Sandbox Code Playgroud)

这些方法正在用相关文本替换组件。为避免这种情况,请使用以下方法之一。请注意单引号 ( ') 和引用的 heredoc ( "EOF") 的使用。

$ echo '${USER}'
${USER}

$ echo '`whoami`'
`whoami`

$ echo '$(whoami)'
$(whoami)

$ cat <<"EOF"
> ${USER}
> EOF
${USER}
Run Code Online (Sandbox Code Playgroud)


xen*_*oid 9

echo "<your command>" | hd应该管用。查找退格 (0x08) 或代码 >=80 的字符。echo "<your command>" | wc -b并检查计数是否与您看到的相符也是一个好主意。

从名称中带有“Office”的任何文件中复制内容是危险的,因为此类软件通常会随意替换字符:在法语中,注意用“guillemets”替换的双引号,在英语中,将纯引号替换为它们的打开/关闭等价物。我发现的最难的一个是文件名中间的 0 宽度不间断空格(服务器停机 3 天......)。

  • 值得一提的是,`hd` 是 `hexdump` 的缩写,Attie 的回答中也提到了它。 (2认同)