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

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

今天早上我在 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
  • 第一个命令是粘贴来自用 gedit 编辑的脚本。
  • 第二个是直接输入在终端。

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

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

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

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


非常感谢您的回答。坏角色是c2 a0不间断空格 UTF-8 字符。问题如何使用 sed 删除特殊的“M-BM-”字符有关于该角色的有趣事实。

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

答案1

一种选择是使用十六进制查看器或编辑器查看您尝试使用的字符。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

您可以在这里看到,,spaceclose-square-brace正确space的 - 0x20,,0x5D0x20

这些值是 ASCII 代码,显示为十六进制. 任何超出范围的值0x20-0x7E都不是可打印字符就 ASCII 而言,很可能无法与命令行界面很好地兼容。

笔记:我抄袭了你的第一个“破碎的“行用于hexdump上述示例,因此某些内容已取代了非 ASCII 空格在原始来源和呈现的问题之间留有一个 ASCII 空格。


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

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

终端和命令行界面不能很好地处理特殊字符 - 正如您所发现的。如果您在格式化文档时不够小心,您也会遇到使用“智能引号“,破折号,列表还在继续......

找出不同之处:(最上面是“智能引号“,底部是”直引号“)

智能引号与直引号的示例

$ 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

此处,开引号不是简单的 ASCII 引号 ( "),而是 Unicode /UTF-8系列- 0xE2,,,或0x800x9CU+201C- 终端将不会像您预期的那样处理。

Kiwy 的建议cat -A也可以起到这样的作用:

$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$

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

例如尝试:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

$ cat <<EOF
> ${USER}
> EOF
attie

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

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

$ echo '`whoami`'
`whoami`

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

$ cat <<"EOF"
> ${USER}
> EOF
${USER}

答案2

您可以使用手册中的选项 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

因此cat -A yourscrip.sh将会向您展示隐形和奇怪的人物。

答案3

echo "<your command>" | hd应该可以工作。查找退格键 (0x08) 或代码 >=80 的字符。echo "<your command>" | wc -b检查计数是否与您看到的内容相匹配也是一个好主意。

复制任何名称中带有“Office”的软件生成的文件都是很危险的,因为此类软件通常会擅自替换字符:在法语中,双引号会被替换为“guillemets”,在英语中,普通引号会被替换为相应的开/关字符。我发现最难的是在文件名中间出现一个宽度为 0 的不间断空格(服务器停机 3 天......)。

答案4

Bash 和其他 shell(如 zsh)可以在编辑器中打开当前命令行。bash 的默认快捷键是C-x C-e( CtrlX CtrlE),它会在 $VISUAL$EDITOR和 emacs 的第一个可用窗口中打开。实际上,这对于调试和修改复杂命令非常有用。根据您的看法,zsh 在这方面比 bash 更友好:当编辑器退出时,bash 会立即运行命令,而 zsh 会等待您按下Enter(为您提供更多机会编辑命令)。

在编辑器中打开命令后,您可以配置编辑器以不同方式显示非 ASCII 字符。

例如,使用 Vim,使用以下设置:

set encoding=latin1
set isprint=
set display+=uhex

在此处输入图片描述

或者,采用其他答案的方法:

bash-4.4$ f() { cat -A "$@"; false; }   # exit false to prevent bash from running the command
bash-4.4$ VISUAL=f
bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true  # C-x C-e here
[ -f /etc/openvpn/client.confM-BM- ] && echo true$

相关内容